Django Edit Form Secure Way - python

I have edit form (modelform) and for that form I created a url something like /contacts/edit//
My view is method base view and I am passing the contact_id from my template to the view as parameter.
Then that's what I am doing:
def edit_contact(request, contact_id):
is_user_authenticated(request.user.is_authenticated)
contact_person = get_object_or_404(domain.Contact.contacts, pk=contact_id)
if request.method == 'POST':
form = forms.ContactPersonForm(request.POST, instance=contact_person)
if form.is_valid():
form.save()
return HttpResponseRedirect('/contacts/')
However that url can be reached by any user with just giving ID and basically you can edit any person you want.
I could do one more query to DB in order to find if a person is the right person to edit contact information but is that right way to do that? What's best practice for my situation?

Related

set the value of a django HiddenInput widget to another object's id rendered in the template

Imagine we have a detail page for the blog posts of a blog and we accept comments on this page, now we need to know which post we are commenting on, in our views so we can make a comment object for that post.
How is possible to set the {{ post.id }} in a HiddenInput widget value so we can then use it in our comment views
I tried to manually add this to my html form but I want to use form template tags so I can validate the form later:
<input type="hidden" name="post_comment" value="{{post.id}}>
forms.py:
comment_post = forms.Field(widget=forms.HiddenInput())
views.py:
def comment(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
Comment.objects.create(text=form.cleaned_data['comment_text'],post=form.cleaned_data['comment_post'] ,cm_author=request.user)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
In general I'd do this by setting the post ID based on something other than a form value. In order to set the post to comment relationship your view has to know which post is being commented on - probably as a URL element - so I'd just use that directly rather than passing it around as form data. Something like:
from django.shortcuts import get_object_or_404
def comment(request, post_id):
post = get_object_or_404(Post, id=post_id)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.post = post
new_comment.save()
# usual form boilerplate below here
One way you could implement this in your uris.py is:
uris:
url(r'(?P<post_id>\d+)/$', views.comment, name="comment")
Depending on the rest of your URL structure it might be clearer to include more context:
url(r'post/(?P<post_id>\d+)/comment/$', views.comment, name="comment")
But that's basically down to personal preference unless you're aiming for a REST-style API.
There's no need to specify the form action differently - the usual action="" will submit the form to a URI that includes the ID, since it's part of the URI that displays the form.
If for some reason you want to do this with the hidden input, use an initial argument when creating the form.
if request.method == 'POST':
form = CommentForm(request.POST, initial={'comment_post': post})
# usual processing
else:
form = CommentForm(initial={'comment_post': post})
# and render
I assume your model Comment has a foreign key relationship with Post, you can just use forms.ModelChoiceField for comment_post:
comment_post = forms.ModelChoiceField(queryset=Post.objects.all(),
widget=forms.HiddenInput())
In the views, give current post object as initial data to the comment_post:
# assume you have a variable that called "current post"
comment_form = CommentForm(request.POST, initial={'comment_post': current_post})
Just in case you are not sure what's the behavior, it's going to create a hidden form field and pre-populate selected data with current_post, then when you POST the form, the form already contains the data, you call comment_form.save() and that's it.

Redirecting from one form to another form in Django

I have two form pages:
Start Form - where you input the basic information
Add product form - uses data from start form to populate some fields. And there you can also give more information in the form.
In the urls.py I have this:
urlpatterns = patterns('',
url(r'^$',add_product_start),
url(r'^add_product/$', add_product),
)
This is my add_product_start form view:
def add_product_start(request):
form = add_product_start_form()
if request.method == "POST":
form = add_product_start_form(request.POST)
if form.is_valid:
#return respose to the add_product url with request data
return render(request,'index.html',{'form':form})
And this is my add_product view:
def add_product(request):
images = []
if request.method == "POST":
initial = {field:value for (field,value) in request._post.iteritems() if value}
#a bunch of other code
#point is I want to receive post data from add_product_start view and at the same time redirect to a different url, because I don't want to have the same url of two different forms.
return render(request,'add_product.html' ,{'form':form,'images':images})
I know there is something like HttpResponseRedirect but they just redirect me to the page without any type of data.I want to get the data from the start form, validate it, and if valid pass it to the second view with a different page.
Can someone please help me. Thank you very much!
The best way is using the form wizard https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/
I solved similar issue using redirect(obj).
This video may help
https://www.youtube.com/watch?v=YsHd-l7QdI8

Passing form data from my view to thank you page

Is there a way I can pass data from a form submission over to the 'thank you' page. The reason i'd like to do this is because I have a form on the website, where the user will select multiple fields which all contains different PDF's.
So once the user has submitted the form the idea is to re-direct them to a thankyou page, where they can view the list of pdf/files they have selected on the form.
I hope this is enough info to go on. Here are my views / models.
def document_request(request, *args):
# template = kwargs['name'] + ".html"
if request.method == 'POST':
form = forms.ReportEnquiryForm(request.POST)
print(request.POST)
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
else:
form = forms.ReportEnquiryForm()
return render_to_response('test.html',{'form':form})
def thank_you(request):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
My initial idea was to pass the data to a new view called thank_you. But not this is possible.
class DocumentUpload(models.Model):
name = models.CharField(max_length="200")
document_upload = models.FileField(upload_to="uploads/documents")
def __unicode__(self):
return "%s" % self.name
class DocumentRequest(models.Model):
name = models.CharField(max_length="200")
company = models.CharField(max_length="200")
job_title = models.CharField(max_length="200")
email = models.EmailField(max_length="200")
report = models.ManyToManyField(DocumentUpload)
def __unicode__(self):
return "%s" % self.name
form.py
class ReportEnquiryForm(forms.ModelForm):
class Meta:
model = models.DocumentRequest
fields = ('name', 'company', 'job_title', 'email', 'report')
If you need anymore info, please ask :)
You've saved the user's submission in a DocumentRequest object. So you can pass the ID of that object in the URL when you redirect, and in the thank_you view you can get the DocumentRequest and render the list.
Edit The idea is to make the thank_you page like any other view that accepts a parameter from the URL:
url(r'thanks/(?P<id>\d+)/$, 'thank_you', name='thank_you')
and so the POST part of the form view becomes:
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
and thank_you is:
def thank_you(request, id):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
Second edit
As others have suggested, this makes it possible for anyone to see the request. So a better solution is to put it in the session:
docrequest = form.save()
request.session['docrequest_id'] = docrequest.id
and in thank_you:
def thank_you(request):
if not 'docrequest_id' in request.session:
return HttpResponseForbidden
docrequest = DocumentRequest.objects.get(request.session['docrequest_id'])
You can do as Daniel Roseman said but in this case the thank you pages can be accessed by anyone with the Ids.
Some ways to pass data between views are the following(the list is not mine.):
GET request - First request hits view1->send data to browser -> browser redirects to view2
POST request - (as you suggested) Same flow as above but is suitable when more data is involved
Using django session variables - This is the simplest to implement
Using client-side cookies - Can be used but there is limitations of how much data can be stored.
Maybe using some shared memory at web server level- Tricky but can be done.
Write data into a file & then the next view can read from that file.
If you can have a stand-alone server, then that server can REST API's to invoke views.
Again if a stand-alone server is possible maybe even message queues would work.
Maybe a cache like memcached can act as mediator. But then if one is going this route, its better to use Django sessions as it hides a whole lot of implementation details.
Lastly, as an extension to point 6, instead of files store data in some persistent storage mechanism like mysql.
The simplest way is to use sessions. Just add the id to the session and redirect to the thank you view, you read the id value and query the db with that id.

POST in django. How does a user update?

I'm brand new to django and fairly new to programming in general. I've done the django tutorial and searched the web for an answer to this question, but to no avail, so now I'm here. I am confused how post works with django. All of the tutorials I've looked at how have a return function in views that displays the webpage. I get that. But then how does a user update data if the page is being rendered from that return statement? After the return there can't be any more updates because the function stops, right? What am I missing here? Any help would be greatly appreciated, I'm getting fairly desperate here.
One pattern for Django views (by no means the only pattern) is to check the request method (GET or POST) at the beginning of the view. If it is POST, then handle the incoming data (before the view returns), and then return either a rendered template, or a redirect.
def view_function(request):
if request.method == 'POST':
if data_is_valid(request.POST):
save_data(request.POST)
return HttpResponseRedirect('/somewhere/good')
else:
return render('template', {'errors': what_went_wrong}
else:
return render('template')
The user updates data in the logic of the view function. That is to say, if the user wishes to update something, you place the update logic in the view function before the return. For example, you would do this:
def update(request):
item = <some model>.objects.get(<something>)
<more code>
return <something>
Usually an edit view function contains two parts -- one for updating data, and the other for displaying the update form. For example,
def user_edit(request):
if request.method == 'POST': # is this a save action?
# save the user data
user_id = request.POST.get('user_id')
username = request.POST.get('username')
description = request.POST.get('description')
user = User.objects.get(id=user_id)
user.username = username
user.description = description
user.save()
return HttpResponseRedirect('/user/') # redirect to index
else:
# show the edit form
user_id = request.GET.get('user_id')
user = User.object.get(id=user_id)
return render_to_response('/user/edit.html', { 'user': user })
There are many different choices for the if request.method == 'POST' line. You can also use if request.POST.get('user_id') to check if specified field is set, to determine if this is a save action.

Display form in Django not based on Models/Form

My view calls some backend classes which need some user input. When user input is required, i halt processing and store the questions into the session - request.session['questions']. request.session['questions'] is a list of dictionaries. e.g.
request.session['question'] = []
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
I need to display these questions to the user along with an input box for each question. When the user submits the form, I need to dump the input into the answers part of the session variable. Could someone show me how to do this? I'm a little lost as this isn't really based on Django forms or models as such.
Thanks
You could use forms that aren't associated with models, like this:
class QuestionForm(forms.Form):
answer = forms.CharField()
def questions(request):
if request.method == 'POST':
form = QuestionForm(request.POST)
if form.is_valid():
# Process the data in form.cleaned_data
return HttpResponseRedirect('/done/')
else:
form = QuestionForm() # An unbound form
return render_to_response('questions.html', {'form': form,})
More documentation here.

Categories

Resources