Django Form resubmit issue when press back button [duplicate] - django

strong textI am trying to prevent a page being cached as I dont want the user to be able to resubmit a form which has already been saved to the db.
I tried the following in views.py
class ServiceCreate(LoginRequiredMixin, CreateView):
model = Service
form_class = ServiceCreateForm
#method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super(ServiceCreate, self).dispatch(request, *args, **kwargs)
However, this has no effect as the entire page is shown instantly when the user hits the back button. How can I prevent caching please?
Update
I'm new to Django so thought that the decorator would instruct the browser not to cache - I did not appreciate that there was server caching as well.

What you're seeing has nothing to do with server caching- it is all browser side.
Ultimately you can't 100% guarantee that a form won't be submitted multiple times (users will find a way...), so you'll have to handle that gracefully on the server. However you can greatly reduce the likelihood of it:
Return an HttpResponseRedirect (or use the redirect shortcut) to redirect the browser after a successful form submission. This will prevent a browser refresh from resubmitting the form.
Use javascript to disable the form submit button after the form as been submitted. I recently had some weird errors and data inconsistencies that turned out to be caused by someone double clicking a form's submit button. Disabling the button after the first click resolved the issue (along with doing more validation server-side to recognize a duplicate submission).
Make sure that you use POST (rather than GET) to submit the form. Browsers are less likely to resubmit the form casually and I believe that Django's CSRF protection should also help prevent errant submissions.

Related

Refresh Django wizard form after browser's back button

I am using SessionWizardView from django-formtools project.
I've noticed that after successfully passing all form checks and executing done() function, which redirects to completely different view, user can still hit browser Back button and re-fill form again.
Isn't there any way to prevent that? I would assume that it would be some kind of session cleaning mechanism. But I cannot find any in documentation.
After some playing around I've found that it can be achieved in two lines:
def done(self, form_list, form_dict, **kwargs):
#regular form processing
self.instance_dict = None
self.storage.reset()
Now, after pressing Back button and submitting form it fails because no data exists and resets to first screen.

Django - How to stay on the same page without refreshing page?

I am using Boostrap modal fade window which renders Django form to update my database records. And what I fail to do is not to reload the page if the user has opened the Update window and did not change anything. It will be easier to get my idea if you look at the code below:
def updateTask(request, task_id):
#cur_usr_sale_point = PersonUnique.objects.filter(employees__employeeuser__auth_user = request.user.id).values_list('agreementemployees__agreement_unique__sale_point_id',flat=True)
selected_task = Tasks.objects.get(id=task_id)
task_form = TaskForm(instance=selected_task )
taskTable = Tasks.objects.all()
if request.method == 'POST':
task_form = TaskForm(request.POST,instance=selected_task)
if task_form.has_changed():
if task_form.is_valid():
# inside your model instance add each field with the wanted value for it
task_form.save();
return HttpResponseRedirect('/task_list/')
else: # The user did not change any data but I still tell Django to
#reload my page, thus wasting my time.
return HttpResponseRedirect('/task_list/')
return render_to_response('task_management/task_list.html',{'createTask_form':task_form, 'task_id': task_id, 'taskTable': taskTable},context_instance=RequestContext(request))
The question is, is there any way to tell Django to change the url (like it happens after redirecting) but not to load the same page with same data for the second time?
It's not trivial, but the basic steps you need are:
Write some javascript to usurp the form submit button click
Call your ajax function which sends data to "checking" view
Write a "checking" view that will check if form data has changed
If data have changed, submit the form
If not, just stay on page
This blog post is a nice walkthrough of the entire process (though targeted towards a different end result, you'll need to modify the view).
And here are some SO answers that will help with the steps above:
Basically:
$('#your-form-id').on('submit', function(event){
event.preventDefault();
your_ajax_function();
});
Call ajax function on form submit
Gotta do yourself!
Submit form after checking

Django prevent page caching

strong textI am trying to prevent a page being cached as I dont want the user to be able to resubmit a form which has already been saved to the db.
I tried the following in views.py
class ServiceCreate(LoginRequiredMixin, CreateView):
model = Service
form_class = ServiceCreateForm
#method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super(ServiceCreate, self).dispatch(request, *args, **kwargs)
However, this has no effect as the entire page is shown instantly when the user hits the back button. How can I prevent caching please?
Update
I'm new to Django so thought that the decorator would instruct the browser not to cache - I did not appreciate that there was server caching as well.
What you're seeing has nothing to do with server caching- it is all browser side.
Ultimately you can't 100% guarantee that a form won't be submitted multiple times (users will find a way...), so you'll have to handle that gracefully on the server. However you can greatly reduce the likelihood of it:
Return an HttpResponseRedirect (or use the redirect shortcut) to redirect the browser after a successful form submission. This will prevent a browser refresh from resubmitting the form.
Use javascript to disable the form submit button after the form as been submitted. I recently had some weird errors and data inconsistencies that turned out to be caused by someone double clicking a form's submit button. Disabling the button after the first click resolved the issue (along with doing more validation server-side to recognize a duplicate submission).
Make sure that you use POST (rather than GET) to submit the form. Browsers are less likely to resubmit the form casually and I believe that Django's CSRF protection should also help prevent errant submissions.

How to add fields to a request?

I have a login page. Upon submission if 'webmail' is selected, the request
should be redirected to the webmail server, with the credentials submitted but
under different keys. Here's what I'm trying now:
if form.validate_on_submit():
if form.destination.data == 'webmail':
form.rc_user.data = form.email.data
form.rc_password.data = form.password.data
return redirect('https://example.com/webmail/', code=307)
This almost works: the POST is redirected to webmail. However the values
submitted are the default values, not the assigned values.
I have some more issues though:
the keys should be _user and _pass, but Flask seems to blow up with
leading-underscore field names.
I do not want to add these fields to the original class. I want to subclass
upon submission, somewhat as follows:
if form.validate_on_submit():
if form.destination.data == 'webmail':
class WebmailLoginForm(LoginForm):
rc_user = EmailField('user', default=form.email.data)
form = WebmailLoginForm(request.form)
return redirect('https://example.com/webmail/', code=307)
When I do this, the added fields show up as UnboundField and are not
submitted.
When issuing a redirect, the browser is simply told to resubmit to another server. I.e. it's too late for the server to influence the request.
So either: start a new request, or use javascript to change the submit target.
Thanks to my colleague Johan for kickstarting my brain.

How to write good form validation in Django?

I've seen Django's samples and I can see they have decent error handling. However I want to see if there is yet a better approach, a general pattern to handle form validation errors in Django. This is the sample I found here:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
In particular, I was wondering:
How can the view in "/thanks/" be sure that the form was validated? Are there any common ways to pass the successful validation of the form to the next view? Or do I need to do something manually such as setting a flag in request's session?
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
EDIT: With regards to #1 I am referring to cases like user manually entering the '/thanks/' url or going back and forth through history pages and accidentally openning it without any form being validated. (Do we still show the "thanks" page? or we need to somehow re-validate why we are in thanks view).
The view can be sure that the form is validated because it will only be called if the form is valid...
If the page is generated through a post request the browser will always ask you that when hitting refresh... I guess the only way to avoid this would be redirecting to another page!
How can the view in "/thanks/" be sure that the form was validated?
form.is_valid() should thoroughly check any field or - if necessary - any combination, cornercase, etc. That's basically it. The views knows, the form was valid if it renders. There is no need to include redundant information in the session.
How can one write this code in a way that when form is NOT valid and the page is shown with errors upon submission, if user refreshes the browser it wouldn't ask the user if they want to POST data again?
I am not sure what the point would be. The form contains errors and the user may correct them or leave. To render a page that would not ask for form resubmission, one could use a redirect, just as in the valid case. The error markup would have to be done manually in that case.