Django POST request is empty - python

Running Django 4.1.1. Having this code below in template. By clicking a button it sends a data to delete apropriate marker.
<form method="POST">
{% csrf_token %}
<ol>
{% for marker in markers %}
<li>
{{ marker }} - <button class="button btn-primary" id="delete" value="{{ marker.pk }}" type="submit">Delete</button>
</li>
{% endfor %}
</ol>
</form>
In views.py
def user_markers(request):
markers = Marker.objects.filter(owner_id=request.user.id).select_related()
if request.method == "POST":
print(request.POST.get("delete")) # gives me None
marker = Marker.objects.get(pk=request.POST.get("delete"))
marker.delete()
context = {
"markers": markers,
}
return render(request, "hub/markers.html", context)
The problem is that request.POST.get("delete") is empty.
POST data has only 'csrfmiddlewaretoken'
Do I miss something?

You need to specify this as name, so:
<button class="button btn-primary" id="delete" name="delete" value="{{ marker.pk }}" type="submit">Delete</button>
If you do not specify a name, it will not be include in the POST data, and thus it will not contain any value that is passed with the button.
You might want to improve the view function:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
#login_required
def user_markers(request):
markers = Marker.objects.filter(owner_id=request.user.id).select_related()
if request.method == 'POST':
get_object_or_404(
Marker, owner=request.user, pk=request.POST.get('delete')
).delete()
return redirect('name-of-some-view') # need to redirect
context = {
'markers': markers,
}
return render(request, 'hub/markers.html', context)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.

Related

Django restricting users from updating/deleting other users posts

views.py
#login_required(login_url='/login')
def updatepost(request,pk):
post = Post.objects.get(id=pk)
form = PostForm(instance=post)
if request.method =='POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
form.save()
return redirect ('mainpage')
context = {'form':form}
return render( request, 'postform.html', context )
postform.html
{% include 'main.html'%}
{% block content %}
{%if user.is_authenticated%}
{% if user.id == post.user.id%}
<div>
<form method="POST" action ="">
{% csrf_token %}
{{form.as_p}}
<input type="Submit" value ="Submit"/>
</form>
</div>
{%endif%}
{%endif%}
{% endblock content %}
I am trying to restrict user who is logged in - from updating , deleting other users posts.
However when I Try to use {% if user.id == post.user.id%} , the page becomes blank even for the user who is editing his own post. The same goes for deleting.
It works on mainpages - where posts are displayed (it hides edit and delete buttons).
What is the reason that the post is not showing inside the template ?
I don't understand that even {{post.user}} in postform.html does not appear , neither on deleteform etc. - why this data of objects of a post is not being sent to postform.html ?
You should change the context to access the post instance from your template.
context = {'form': form, 'post': post}
You can only access the context values from the templates. So pass the ones you want to use while you are returning a response.

Django checkout not accessible: Page not found (404)

I'm trying to develop an e-commerce site with Django. So I'm at this point where, users can add items to their cart, but when I try to proceed to checkout, for some reason, my checkout form is not displayed rather, it says:
Page not found (404)
I made sure that I have registered my models, and ran migrations.
What is the problem?
My views.py:
#login_required
def checkout(request):
address_form = UserAddressForm(request.POST or None)
if address_form.is_valid():
new_address = address_form.save(commit= False)
new_address.user = request.user
new_address.save()
else:
raise Http404
print(form.errors)
context = {"address_form": address_form}
template = "orders/checkout.html"
return render(request, template, context)
My checkout.html:
<form method="POST" action=''>
{% csrf_token %}
<fieldset class="form-group">
{{ address_form|crispy }}
</fieldset>
<div class="form-group">
<input type="submit" class="btn btn-outline-dark" value="Place Order"/>
</div>
</form>
My urls.py:
from orders import views as orders_views
path('checkout/', orders_views.checkout, name='checkout'),
You've implemented GET request handling incorrectly, for reference see this example from the docs. In your case form was always invalid because in case of GET request it was initialized with none. However you don't even have to validate empty form on GET request.
Your code updated:
#login_required
def checkout(request):
if request.method == 'POST':
address_form = UserAddressForm(request.POST)
if address_form.is_valid():
new_address = address_form.save(commit= False)
new_address.user = request.user
new_address.save()
return # TODO : return what?
else:
# otherwise (if GET request) we get here
address_form = UserAddressForm()
context = {"address_form": address_form}
return render(request, "orders/checkout.html", context)
And you need to specify what is supposed to happen when the form is valid: redirect for example.

Unable to update CharField - Django

First of all I'm glad to be here, I read you lately and i found useful answers here.
This is my first post so please be kind with me, I'm a newbie in programming.
So, I'm writing my 1st web application in Django - a todo app and I don't know how to write the function that does this this. I found something in Django docs and in other related discussions but it doesn't work.
Here's my code:
#models.py
class Task(models.Model):
user = models.ForeignKey(User)
task = models.CharField(max_length=200)
initialized_at = models.DateTimeField(auto_now_add=True)
due_date = models.DateField(default=datetime.now)
done = models.BooleanField(default=False)
def __unicode__(self):
return self.task
#views.py
def edit_task(request, id):
if request.method == 'POST':
task_to_edit = Task.objects.get(pk=task_id)
form = TaskForm(request.POST, instance=task_to_edit)
form.save()
if form.is_valid():
task_to_edit = form.save()
return HttpResponseRedirect('/')
else:
form = TaskForm()
return render(request, 'todo/edit_task.html', {'form': form})
#urls.py
url(r'^edit_task/(?P<task_id>\w+)/$', 'todo.views.edit_task')
#edit_task.html
{% block content %}
<form action="/edit_task/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}
When I submit the updated form I get this error:
Page not found (404)
Request Method: POST
Request URL: hxxp://127.0.0.1:8000/edit_task/
Using the URLconf defined in jbz.urls, Django tried these URL patterns, in this order:
^admin/
^$ [name='index']
^(?P<task_id>\d+)/$
^(?P<task_id>\d+)/$
^add-task/$
^delete-task/(?P<task_id>\w+)/$
^edit_task/(?P<id>\w+)/$
^done/(?P<task_id>\d*)/$
The current URL, edit_task/, didn't match any of these.
and the root urls.py looks like:
url(r'', include('todo.urls'))
#edit_task.html
{% block content %}
<form action="/edit_task/{{task.id}}" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}
Notice how I added {{task.id}} expression in <form action="/edit_task/{{task.id}}" method="post">
IMPORTANT NOTE: Substitute {{task.id}} to whatever variable accomplishes this in your template.
The reason why you get the error is because edit_task/ is not getting the other part, task_id to match the regular expression:
url(r'^edit_task/(?P<task_id>\w+)/$', 'todo.views.edit_task')
UPDATE: Also your edit_task view has potential errors as well>
def edit_task(request, id):
task_to_edit = Task.objects.get(pk=id)
if request.method == 'POST':
form = TaskForm(request.POST, instance=task_to_edit)
form.save()
if form.is_valid():
task_to_edit = form.save()
return HttpResponseRedirect('/')
else:
form = TaskForm(instance=task_to_edit)
# you don't pass any task variable to the view so the form view
# won't know which task to edit, you'll have to handle that
return render(request, 'todo/edit_task.html', {'form': form, 'task':task_to_edit})
Note: I corrected the code in the view a little. Now the task_to_edit is passed also to the Form to fill the fields when the view is requested via GET. Notice that in order to access to this view, the url in the browser should look like this http://www.example.com/edit_task/2
If other wise you try to access http://www.example.com/edit_task without passing the id you'll get Error 404.
Hope this helps!
I think your pattern for edit task expects an id - task name. Try changing your URL pattern:
'^edit_task/(?P<task_id>\w+)/$'
to
'^edit_task/$'
or providing the task id that you want to edit.
Just add name space to your url and according update your template.
#urls.py
url(r'^edit_task/(?P<task_id>\w+)/$', 'todo.views.edit_task', name= "edit_task")
#edit_task.html
{% block content %}
<form action="{% url 'edit_task' task_id %}" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}

Djangobook ch 7 CSRF

I'm reading the Djangobook and I'm on ch 7.There is actually a line that says "#todo - explain CSRF token"
When I was following the examples (I'm pretty sure I've followed them exactly), I cannot get the code to function properly.
Here is my template
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/contact/" method="post">
{% csrf_token %}
<p>Subject: <input type="text" name="subject"></p>
<p>Your e-mail (optional): <input type="text" name="email"></p>
<p>Message: <textarea name="message" rows="10" cols="50"></textarea></p>
<input type="submit" value="Submit">
</form>
</body>
</html>
Here is my view
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template import RequestContext
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '#' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply#example.com'),
['siteowner#example.com'],
)
return HttpResponseRedirect('/contact/thanks/')
return render(request, 'contact_form.html',
{'errors': errors}, context_instance=RequestContext(request))
This is the error I'm getting
Forbidden (403)
CSRF verification failed. Request aborted.
Help
Reason given for failure:
CSRF token missing or incorrect.
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
- Your browser is accepting cookies.
- The view function uses RequestContext for the template, instead of Context.
- In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
- If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.
EDIT******
I discovered that I can view the source code for my form and the csrf_token isn't being inserted even though I have it in my template. I looked up common solutions. Some people suggested I do this
return render_to_response('contact_form.html',
{'errors': errors}, context_instance=RequestContext(request))
But this doesn't work for me either.
I just checked my settings.py and I see 2 middlewares added not just CsrfViewMiddleware -
MIDDLEWARE_CLASSES = (
...
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.middleware.csrf.CsrfResponseMiddleware’,
)
Try adding more.

WTforms form not submitting but outputs no validation errors

I'm trying to get file uploads with flask-uploads working and running in to some snags. I'll show you my flask view function, the html and hopefully someone can point out what I'm missing.
Basically what happens is that I submit the form and it fails the if request.method == 'POST' and form.validate(): check in the view function. It jumps down to display the template. wtforms isn't kicking me any errors on the form so I'm wondering why its failing that if statement.
What am I over looking?
Setting up flask-uploads:
# Flask-Uploads
photos = UploadSet('photos', IMAGES)
configure_uploads(app, (photos))
View:
def backend_uploadphoto():
from Application import photos
from Application.forms.backend import AddPhotoForm
clients = Client.query.all()
events = Event.query.order_by('date').all()
form = AddPhotoForm(request.form, csrf_enabled=True)
if request.method == 'POST' and form.validate():
from uuid import uuid4
uuid = uuid4()
filename = '{0}.jpg'.format(uuid)
photo = Photo(uid=uuid, client=request.form['client'], event=request.form['event'])
photofile = photos.save(request.files.get('photo'), photo.filename)
return redirect(url_for('backend'))
return render_template('backend/addphoto.html', form=form, clients=clients, events=events)
Form:
class AddPhotoForm(Form):
photo = FileField('Photo')
client = IntegerField('Client:')
event = IntegerField('Event:')
HTML:
<form action="{{url_for('backend_uploadphoto')}}" method="post">
<p>
{{form.client.label}}
<select name="client">
{% for client in clients %}
<option value="{{client.id}}">{{client.fullname}}</option>
{% endfor %}
</select>
{{form.client.errors}}
</p>
<p>
{{form.event.label}}
<select name="event">
{% for event in events %}
<option value="{{event.id}}">{{event.name}}</option>
{% endfor %}
</select>
{{form.event.errors}}
</p>
<p><label for="photo">Photo:</label>{{form.photo}} <input type="submit" value="Upload"> {{form.photo.errors}}</p>
</form>
You have csrf_enabled=True but your form doesn't have any CSRF protection since you aren't inheriting from SecureForm. If you want to enable CSRF, read the documentation and update your form definition.
If this was unintended, you can remove csrf_enabled=True and your logic will work as expected.
To enable CSRF protection, there are a few steps:
Inherit from SecureForm
Create the generate_csrf_token and validate_csrf_token methods in your form. These methods will generate a unique key and raise errors when it doesn't validate.
Add {{ form.csrf_token }} to your template.

Categories

Resources