Django add form on every page - django

In my application i need to add a form in base.html, which I've done. For this i used context_processors, now my problem is everytime i'm trying to post, i'm getting a blank page and this error: Method Not Allowed (POST)
In this form i want just to add a button where it will mark all current users notifications as read.
I know that you can use context_processors like this:
def my_context(request):
data = dict()
if request.user.is_authenticated:
data['notifications'] = Notification.objects.filter(user=request.user, read=False)
data['form'] = NotificationForm()
return data
But instead of adding the form i need these lines:
def my_context(request):
data = dict()
if request.user.is_authenticated:
data['notifications'] = Notification.objects.filter(user=request.user, read=False)
if request.method == 'POST':
if 'read-notifications' in request.POST:
for notification in data['notifications']:
notification.read = True
notification.save()
next = request.POST.get('next', '/')
return redirect(next)
return data
The form in base.html:
<form action="" method="POST">{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="btn m-btn--square btn-primary" name="read-notifications">Mark as read</button>
</form>
urls.py
url(r'^orders/create/$', views.admin_order_document, name='create_order'),
url(r'^orders/waiting/$', views.OrdersWaitingListView.as_view(), name='order_waiting_list'),
url(r'^orders/unallocated/$', views.OrdersUnallocatedListView.as_view(), name='order_unallocated_list'),
url(r'^orders/working/$', views.OrdersWorkingListView.as_view(), name='order_working_list'),
How can i show this form on every page without getting the above error?

So, I chose a different approach and i ended up with this solution:
base.html
<form action="{% url 'mark_read' %}" method="POST">{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="btn m-btn--square btn-primary" name="action" value="mark_read">Marchează ca citite</button>
</form>
views.py
#login_required()
def read_notifications(request):
if request.method == 'POST':
if 'action' in request.POST:
notifications = Notification.objects.filter(user=request.user, read=False)
for notification in notifications:
notification.read = True
notification.save()
next = request.POST.get('next', '/')
return redirect(next)
urls.py
url(r'^mark-read/$', views.read_notifications, name='mark_read'),
Basically, the form action will redirect me to the view function, it will do what it has to do and it will redirect me back on the same page.

Related

How to get the value of a specific field in a form in Django view function?

I am trying to achieve a load-up process in my system where the user will input the load amount and add it to a user's current load.
How can I get the amount entered in my view function?
Here's my function in my views.py
def LoadWallet(request, pk):
user = get_object_or_404(User, id=request.POST.get('user_id'))
user_wallet = user.wallet
if request.method == 'POST':
form = LoadForm(request.POST)
if form.is_valid():
user_wallet = user_wallet+form.instance.load_amount
User.objects.filter(id=pk).update(wallet=user_wallet)
return HttpResponseRedirect(reverse('user-details', args=[str(pk)]))
and the form in my template file
<form action="{% url 'load-wallet' user.pk %}" method="POST">
{% csrf_token %}
<label for="load_amount">Load amount</label>
<input type="text" class="form-control" id="load_amount" onkeyup="replaceNoneNumeric('load_amount')">
<button type="submit" name="user_id" value="{{ user.id }}" class="btn btn-md btn-success" style="float: right; margin: 10px 5px;">Load</button>
</form>
Right now I tried this but it's returning "name 'LoadForm' is not defined". Should I declare the LoadForm first?
Is there a better way to implement this? Thank you!
You might have an easier time using something like this, than LoadForm:
def LoadWallet(request, pk):
user = get_object_or_404(User, id=request.POST.get('user_id'))
user_wallet = user.wallet
if request.method == "POST":
user_id = request.POST["user_id"]
# Other logic here
return ...
And in template
<form class="load-wallet" action="" method="POST">
{% csrf_token %}
<input type="text" name="user_id" placeholder="What is the user id?">
<button type="submit" class="submit-btn"> Submit </button>
</form>

Reuse the django form request

I have a page that displays a report according to the informed criteria, within that same page I would like to create a "send by email" button, how could I do to take advantage of the same request?
You can create a seperate function that handles a POST request and then in your form bind the action attribute to that function, for example:
urls.py
urlpatterns = [
path('handle_second_form/', views.handle_second_form, name="handle_second_form"),
]
views.py
def handle_second_form(request):
if request.method == 'POST':
data = request.POST.get('data')
return render(request, 'your_template.html', {'data': data})
Then in your template add a second form like this:
index.html
<form action="{% url 'handle_second_form %}" method="post">
{% csrf_token %}
<input type="text" name="data" placeholder="Enter your data">
<button type="submit">Submit</button>
</form>

Django #login_required causing issue when submitting forms

I am passing a some information to view function by submitting a form and view requires has #login_required decorator. Here is the template where i'm passing email ID using a from
<form action="{% url 'become_booster' %}" method="post">
{% csrf_token %}
<input type="hidden" name="email" value="{{ profile_user.email }}" />
<div class="form-group">
<div class="col-md-12 col-sm-12">
<input type="submit" class="btn btn-success btn-sm" value="Become a Booster">
</div>
</div>
</form>
Here is the view function
#login_required
def become_booster(request):
if request.method == "POST":
email = request.POST.get('email')
user = CustomUser.objects.filter(email= email)[0]
tiers = Tiers.objects.filter(user=user)
form = SubscriptionForm
return render(request,'select_tier.html',{'tiers':tiers,'form':form})
This is working fine when the user logedin already. When user didn't login, #login_required sends them to login and when it comes back request.method is no longer POST. It no longer has that email info. Can someone help me with this. Thanks!
#login_required decorator is used to available that function to registered user who are actually logged in or send to login page or registration page. if you remove #login_required from your view function it will be available for unregistered and all type of users.
Try this,
from django.shortcuts import *
#login_required(login_url='become_booster')
def become_booster(request):
if request.method == "POST":
email = request.POST.get('email')
user = get_object_or_404(CustomUser, email=email)
tiers = get_list_or_404(Tiers, user=user)
form = SubscriptionForm
return render(request,'select_tier.html',{'tiers':tiers,'form':form})
elif request.method == 'GET':
# Something like this
# return render(request, 'your_template.html', {'profile_user': user_object_which_contains_email})

Django: Transmit GET-param after form-submit

I call my page with http://localhost:63314/user/mieter/?wohnungseinheit=1 as a(GET)-parameter.
I would like to use the transmitted parameter as an assignment for the "wohnungseinheit". After I filled in and sent my "form", the GET-parameter is missing.
How can I assign the "wohnungseinheit"?
def mieter(request,id):
if request.method == 'POST':
form = MieterForm(request.POST)
if form.is_valid():
tmp = request.GET.get('wohnungseinheit')
mieter = form.save()#wohnungseinheit=tmp
print(tmp) #result: None
Wohnungseinheit = Wohnungseinheiten.objects.get(id=tmp)
Wohnungseinheit.mieter.add(mieter)
return render(request,'Immo/user/mieter.html',{'form':form}) # i think i also could use render_to_response
else:
if not str == None and str(id).isdigit():
#unimportant code here
if blnAllowedAccess:
form = MieterForm(request.POST)
return render(request,'Immo/user/mieter.html',{'form':form})
else:
#NOTALLOWEDTOACCESS! TODO
pass
else:
tmp = request.GET.get('wohnungseinheit')
if tmp is not None:
form = MieterForm(request.POST or None)
return render(request,'Immo/user/mieter.html',{'form':form})
else:
pass #TODO: 404-error
EDIT:
models.py:
class MieterForm(forms.ModelForm):
nameL = forms.CharField(required=True,max_length=100)
class Meta:
model=Mieter
fields = ("nameL",)
class Wohnungseinheiten(models.Model):
mieter = models.ManyToManyField(Mieter,blank=True,null=True)
urls.py:
re_path(r'user/mieter/(?:(?P<id>\w+)/)?$', views.mieter,name="mieter"),
user/mieter.html:
<form class="form-signin" action="{% url 'mieter' %}" method="post">{% csrf_token %}
<input class="form-control" id="{{ form.nameL.auto_id }}" name="nameL" value="{{ form.nameL.value }} "type="text">
<button type="submit" class="btn btn-primary mb-2">Submit</button>
</form>
The solution was: <a href="{% url 'mieter' %}?wohnungseinheit=1">
The param could be dynamically assembled by an "if" or "for".

How to redirect to previous page in Django after POST request

I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.
View that links with button:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.
You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit">Let's Go</button>
</form>
views.py
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
This is roughly what django.contrib.auth does for the login form if I remember well.
If you pass through an intermediate page, you can pass the 'next' value via the querystring:
some_page.html
Go to my form!
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">Let's Go</button>
</form>
You can use the HTTP_REFERER value:
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.
You can use this
return redirect(request.META.get('HTTP_REFERER'))
Make sure to import this
from django.shortcuts import redirect
My favorite way to do that is giving the request.path as GET parameter to the form.
It will pass it when posting until you redirect.
In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url.
Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.
Example urls.py:
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('user/update/', UserUpdateView.as_view(), name='user_update'),
]
Link to the form inside of the template:
Update User
Class-Based-View:
class UserUpdateView(UpdateView):
...
def get_success_url(self):
return self.request.GET.get('next', reverse('home'))
In your function based view you can use it as follows:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
next = request.GET.get('next', reverse('home'))
return HttpResponseRedirect(next)
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
you could do this easily with a simple one-liner JS
<button onclick="history.back()">Go Back</button>
This will take you back to the previous page of your history list.
If you don't have a history
https://www.w3schools.com/jsref/met_his_back.asp
Use HTTP_REFERER value:
for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
for use in template Go Back
In case this helps someone I got this to work in class based UpdateView
template
<form class="form" method="POST">
{% csrf_token %}
<!-- hidden form field -->
<input type="hidden" id="previous_page" name="previous_page"
value="/previous/page/url">
<!-- any other form fields -->
{{ form.name|as_crispy_field }}
{{ form.address|as_crispy_field }}
<!-- form submit button -->
<button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>
<!-- JS to insert previous page url in hidden input field -->
<script>
prev = document.getElementById("previous_page");
prev.value = document.referrer;
</script>
views.py
class ArticleUpdateView(generic.UpdateView):
model = Article
form_class = ArticleForm
template_name = 'repo/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
# if form is valid get url of previous page from hidden input field
# and assign to success url
self.success_url = self.request.POST.get('previous_page')
return super().form_valid(form)
The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.