[Fixed]-Prevent multiple form submissions in Django

21👍

Client side, start with JavaScript. You can never trust the client, but its a start.

i.e.

onclick="this.disabled=true,this.form.submit();

Server side you ‘could’ insert something into a database i.e. a checksum. If its a records you are insert into a database use model.objects.get_or_create() to force the uniqueness on database level you should use unique_together.

Lastly: HTTPRedirect is best, The the method I use when a user processes a payment is to just issue a HTTPRedirect() to the thank you/conformation page. This way refreshing the form won’t resubmit and if they go back and try to submit the form again (without refreshing the form) the Django Cross Site Request Forgery (CSRF) will fail, perfect!

4👍

I also try to find a good way to prevent double records generation when a user dbl-click on a submit button.
It’s not about the PRG issue that is easily fixed by redirection.

So, regards on this basic concern, the solution with HTTPRedirect on server-side doesn’t help.

On client-side, I found two problems when I disable the button before submit:

  1. With HTML5 validation, the form.submit() will be interupted by browser if the form is invalid => submit button is still disabled=true.
  2. When the user submit the form and do a back in browser history, the DOM will be loaded from browser’s cache => submit button is still disabled=true.

So here is my workaround for the first client-side problem (HTML5 validation):

isFormHtml5Valid(form) {
  for(var el of form.querySelectorAll('input,textarea,select')){
    if(!el.checkValidity())
      return false;
  }
  return true;
}

mySubmitButton.onclick = function() {
  if(this.form && isFormHtml5Valid(this.form))
    this.disabled=true;
  this.form.submit();
}

I try to find a client-side workaround for the second client-side problem (browser cache the DOM) but nothing worked (onbeforeunload, …).
So the workaround I currently use for “browser cache” issue is add a @never_cache decoration on the top of concerned views (from server-side, indicate to client-side to not caching). Please let me know if you have a better workaround.

Last but not least, I would really appreciate to fix this issue on server side.
The CSRF solution seems not suitable since a CSRF token is generated by session (not for each form).
So here is the status of my work and my question:

  • Fix this issue on client-side is OK but doesn’t look like a good solution to me. How could we avoid to validate this multiple form submition on server-side?

Let me know if you have a good solution for that.


Edit 1:
May be a small part of the answer: Synchronizer (or Déjà vu) Token

But I didn’t find any implemantation of that in Django.

👤Gosti

1👍

Use HttpResponseRedirect

create a new view(lets say thank_you) for successful message to display after form submission and return a template.

After successful form submission do return HttpResponseRedirect(“/thank-you/”) to the new thank-you view

from django.http import HttpResponseRedirect

def thank_you(request, template_name='thank-you.html'):
    return render_to_response(template_name,locals(),context_instance=RequestContext(request))

and in urls.py

url(r'^thank-you/$','thank_you', name="thank_you")

Multiple form submission happens because when page refreshes that same url hits, which call that same view again and again and hence multiple entries saved in database. To prevent this, we are required to redirect the response to the new url/view, so that next time page refreshes it will hit that new url/view.

0👍

  1. After a successful submission respond with a redirect so a reload doesn’t cause another submission
  2. Disable the submit button on form submission

As for #2, it’s best to do that in the onsubmit handler and not the onclick for the submit button. This will handle cases such as multiple submit buttons or if there’s any HTML5 client-side form validation. In jQuery it’d look something like:

$('#edit_form').submit( function(event) {
    // disable to avoid double submission
    $('#submit_button').attr('disabled', true);
});

However, one issue this doesn’t account for is if the user cancels the submit part way. For example, if you click “submit” and then immediately hit “Esc”, the browser will stop the submission but “submit” button will already be disabled.

Also, it’s possible to have a form that is submitted by hitting “enter” and this solution wouldn’t prevent that form from submitting twice.

Leave a comment