11

I recently upgraded to Django 1.2.3 and my upload forms are now broken. Whenever I attempt to upload, I receive a "CSRF verification failed. Request aborted." error message.

After reading Django's documentation on this subject, it states that I need to add the {% csrf_token %} template tag within the HTML <form> in my template. Unfortunately, my <form> is generated via JavaScript (specifically, ExtJs's "html" property on a Panel).

Long story short, how do I add the required CSRF token tag to my <form> when my <form> is not included in a Django template?

Huuuze
  • 15,528
  • 25
  • 72
  • 91

6 Answers6

13

Another option would be to adapt the cookie/header based solution shown in the Django docs with Ext - preferable if you have a lot of templates and don't want to change every single one.

Just drop the following snippet in your overrides.js (or wherever you put global modifications):

Ext.Ajax.on('beforerequest', function (conn, options) {
   if (!(/^http:.*/.test(options.url) || /^https:.*/.test(options.url))) {
     if (typeof(options.headers) == "undefined") {
       options.headers = {'X-CSRFToken': Ext.util.Cookies.get('csrftoken')};
     } else {
       options.headers.extend({'X-CSRFToken': Ext.util.Cookies.get('csrftoken')});
     }                        
   }
}, this);

(edit: Ext already has cookie reading function, no need to duplicate it)

chrisv
  • 1,447
  • 17
  • 21
  • Do I need to do/add anything to my dynamically generated form? – john2x Apr 06 '11 at 09:57
  • 1
    @john2x: Nope, you just need to put the code in your overrides.js. If you are not familiar with the concept of overrides.js, the following blog is a good start: http://edspencer.net/2009/07/extoverride-monkey-patching-ext-js.html – chrisv Apr 07 '11 at 11:03
  • +1. Brilliant, I was thinking on the same lines when I found this answer! – Swanand Apr 05 '12 at 15:57
  • Also worth noting that `csrftoken` cookie might not be present if your template doesn't contain `{% csrf_token %}`. To force always setting the cookie decorate your views with `@ensure_csrf_cookie` imported as `from django.views.decorators.csrf import ensure_csrf_cookie`. See https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax – serg Oct 14 '15 at 21:39
9

The easiest way is to create a hidden form on your page using django that doesn't do anything. Then use JavaScript to fetch the form and specifically the token input out of the form. Lastly, insert or copy that token input into the form you are dynamically generating.

Here are two examples of how you might publish the token for JavaScript.

<input id="csrf_token" value="{{ csrf_token }}"/>

<script type="text/javascript">
var CSRF_TOKEN = document.getElementById('csrf_token').value;
</script>

or

<script type="text/javascript">
var CSRF_TOKEN = "{{ csrf_token }}";
</script>
matt snider
  • 4,013
  • 4
  • 24
  • 39
  • @Huuuze: put `{csrf_token}` in the template somewhere, and parse it with JavaScript during form building. – viam0Zah Sep 22 '10 at 05:45
1

A better solution is to generate the code of the js file from a view/template.

Then, in the view you can set the csrf token up in the context like so...

from django.core.context_processors import csrf
context = RequestContext(request)
context.update(csrf(request))

Then, in the template, you can use {{ csrf_token }} to get the raw value of the csrf token, and then use that to build a hidden field into your form with the name csrfmiddlewaretoken.

Olli
  • 1,231
  • 15
  • 31
fatgeekuk
  • 403
  • 2
  • 5
0

This will use the csrf token or auto generate a new one if needed. It will handle all form submits on the page. If you have off-site forms you'll need to make sure they don't run this code.

<script>
$(document).on('submit', 'form[method=post]', function(){
  if(!document.cookie.match('csrftoken=([a-zA-Z0-9]{32})')) {
    for(var c = ''; c.length < 32;) c += 'abcdefghijklmnopqrstuvwxyz'.charAt(Math.random() * 26)
    document.cookie = 'csrftoken=' + c + '; path=/'
  }
  if(!this.csrfmiddlewaretoken) $(this).append('<input type="hidden" name="csrfmiddlewaretoken">')
  $(this.csrfmiddlewaretoken).val(document.cookie.match('csrftoken=([a-zA-Z0-9]{32})')[1])
})
</script>

requires jQuery 1.7+

Collin Anderson
  • 14,787
  • 6
  • 68
  • 57
0

Does the view you are POSTing to also respond to GET? In that the JS code can make a GET request to the view in question and parse the output to extract the CSRF token. My JS-fu is weak and I am not sure how best you can do the parsing from the client side.

For a broadly related example see this question. In this case the user was attempting to POST using a Python script and failing for the same reason. The solution was the same, except he had to do it from a Python script rather than JavaScript.

Community
  • 1
  • 1
Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
0

This may not be ideal, but it was the fastest solution for me. In my main template at the bottom of the "body", I added a javascript function to my library.

<script type="text/javascript">
    MyToolkit.Utils.getCSRFToken = function () {
         return "{% csrf_token %}";
    };
</script>
Dave
  • 1,196
  • 15
  • 22