I could use some help complying with Django's CSRF protection mechanism via my AJAX post. I've followed the directions here:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
I've copied the AJAX sample code they have on that page exactly:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
I put an alert printing the contents of getCookie('csrftoken')
before the xhr.setRequestHeader
call and it is indeed populated with some data. I'm not sure how to verify that the token is correct, but I'm encouraged that it's finding and sending something.
But Django is still rejecting my AJAX post.
Here's my JavaScript:
$.post("/memorize/", data, function (result) {
if (result != "failure") {
get_random_card();
}
else {
alert("Failed to save card data.");
}
});
Here's the error I'm seeing from Django:
[23/Feb/2011 22:08:29] "POST /memorize/ HTTP/1.1" 403 2332
I'm sure I'm missing something, and maybe it's simple, but I don't know what it is. I've searched around SO and saw some information about turning off the CSRF check for my view via the csrf_exempt
decorator, but I find that unappealing. I've tried that out and it works, but I'd rather get my POST to work the way Django was designed to expect it, if possible.
Just in case it's helpful, here's the gist of what my view is doing:
def myview(request):
profile = request.user.profile
if request.method == 'POST':
"""
Process the post...
"""
return HttpResponseRedirect('/memorize/')
else: # request.method == 'GET'
ajax = request.GET.has_key('ajax')
"""
Some irrelevent code...
"""
if ajax:
response = HttpResponse()
profile.get_stack_json(response)
return response
else:
"""
Get data to send along with the content of the page.
"""
return render_to_response('memorize/memorize.html',
""" My data """
context_instance=RequestContext(request))
Thanks for your replies!
As it is not stated anywhere in the current answers, the fastest solution if you are not embedding js into your template is:
Put
<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
before your reference to script.js file in your template, then addcsrfmiddlewaretoken
into yourdata
dictionary in your js file:One CSRF token is assigned to every session ( i.e. every time you log in). So before you wish to get some data entered by user and send that as ajax call to some function which is protected by csrf_protect decorator, try to find the functions that are being called before you are getting this data from user. E.g. some template must be being rendered on which your user is entering data. That template is being rendered by some function. In this function you can get csrf token as follows: csrf = request.COOKIES['csrftoken'] Now pass this csrf value in context dictionary against which template in question is being rendered. Now in that template write this line: Now in your javascript function, before making ajax request, write this: var csrf = $('#csrf').val() this will pick value of token passed to template and store it in variable csrf. Now while making ajax call, in your post data, pass this value as well : "csrfmiddlewaretoken": csrf
This will work even if you are not implementing django forms.
In fact, logic over here is : You need token which you can get from request. So you just need to figure out the function being called immediately after log in. Once you have this token, either make another ajax call to get it or pass it to some template which is accessible by your ajax.
If you use the
$.ajax
function, you can simply add thecsrf
token in the data body:Here's a less verbose solution provided by Django:
Source: https://docs.djangoproject.com/en/1.11/ref/csrf/
If your form posts correctly in Django without JS, you should be able to progressively enhance it with ajax without any hacking or messy passing of the csrf token. Just serialize the whole form and that will automatically pick up all your form fields including the hidden csrf field:
I've tested this with Django 1.3+ and jQuery 1.5+. Obviously this will work for any HTML form, not just Django apps.
for someone who comes across this and is trying to debug:
1) the django csrf check (assuming you're sending one) is here
2) In my case,
settings.CSRF_HEADER_NAME
was set to 'HTTP_X_CSRFTOKEN' and my AJAX call was sending a header named 'HTTP_X_CSRF_TOKEN' so stuff wasn't working. I could either change it in the AJAX call, or django setting.3) If you opt to change it server-side, find your install location of django and throw a breakpoint in the
csrf middleware
.f you're usingvirtualenv
, it'll be something like:~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.py
Then, make sure the
csrf
token is correctly sourced from request.META4) If you need to change your header, etc - change that variable in your settings file