0

I keep getting 403 Errors from Django.

I set up my settings.py to use the CSRF protection, and used the csrf_token token in my template.

Here is the JS file I included just after the HTML header: http://bpaste.net/show/87791/

Using Firebug I can check that the CSRF cookie is there. Later on the page, the user clicks on a button that triggers this code:

myFunction: function() {
  $.ajax({
  type: 'POST',
  url: window.localtion.href + 'myajaxview',
  async: false
  });
}

I am using a simple class based view inheriting from TemplateView to display this page. 'myajaxview' is inheriting from View and a JSON Mixin. However its code is never executed since django cannot validate the CSRF token.

It seems to me that the ajax doesn't send the token with the POST headers as it should. Or am I missing something?

EDIT: I moved the $.ajaxSetup call just before the call to the $.ajax() POST function and it worked. I tried to move it somewhere else and it failed. The problem is more related to Ajax than Django I think. So, my question is still there, I don't want to put the $.ajaxSetup call before each $.ajax call, I don't think it is the way things are done, I don't want to repeat myself. So this was just a workaround and I am asking for the solution.

2
  • You might want to take a look at csrf_exempt here Commented Mar 30, 2013 at 17:27
  • Already saw that, if you look at my code on bpaste it is almost the same. The difference is that I include that as an external .js file. Since I want to share that feature and not include the same code each time I do an ajax request in each template. Maybe that's the issue? Where to put the csrf_token when using ajax? And where to put the getCookie code (i.e: only if I did it wrong)? Commented Mar 30, 2013 at 18:02

5 Answers 5

3
myFunction: function() {
  $.ajax({
  type: 'POST',
  data: { 
    'csrfmiddlewaretoken': '{{csrf_token}}'
  }, 
  url: '/myajaxview/',
  async: false
  });
}
Sign up to request clarification or add additional context in comments.

2 Comments

This is like limelights's answer, it works I guess and will help others, but I try to do it the way the documentation tells it: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. Which is why I struggle with beforeSend()..
Well it works for now. I think the hardest part is to ensure the beforeSend() is not overridden by a library, especially when using async includes. Yet it works for me now (see my answer). Thanks for your input though, I might still use it in some scenarios!
2

This is the snippet of code that I think you are looking for

var csrfToken = $('input[name="csrfmiddlewaretoken"]').val();
$.ajax({
    url: 'blah.com/',
    //snipped for brevity
    csrfmiddlewaretoken: csrfToken,
    success: function(data){ doStuff(); }
});

This is what I use in order to be able to do jquery ajax calls to a django app. Hope it works!

3 Comments

I appreciate it. I can use getCookie() instead of your method to get the csrfToken right? But still, I am puzzled as to why the xfer.setRequestHeader() doesn't work in beforeSend(). If I make it work then I don't need to manually add the csrfmiddlewaretoken.
You could use it to get the csrf token but the xhr.beforesend setup thing doesn't work for me either. I believe it's because the csrf_token isn't generated properly when using dynamically loaded forms or dom elements. I'll look into it later today and provide an update for you :)
Looks like it doesn't work well, although it's on the official django documentation. I think I found a solution to make it work, but I am not sure I solved everybody's problem since mine was specific.
1

Thank you for your help. The answer is really nasty, I found it out by beginning with a simple example to test ajaxSetup + ajax and then I added more complexity to match my original code.

It was because of jquery-tools. When I began using bootstrap-twitter I saw that they advise to put javascript includes at the end of the page. So I put the jquery-tools.js include there as well. Little did I know that in this script file a call to $.ajaxSetup was made which overrode my own.

The solution is to put the jquery tools include at the top level. However at this point I am not sure how much it will conflict with my code. Since I need the ajaxSetup for each ajax request I will do.

It took me a day to find out, I was on #jquery and #django and a lot of people went out of their way to try to find out the solution. If you ever get a complex code base that you cannot share and a problem you want to solve here's my advice: try to make the simplest example working and change it until it matches your failing setup. It will save everybody's time, above all your own.

Comments

0

First, did you check if the CSRF code is actually send to the server? If it is, does the server expect the hash to be in the post values, or in the header? Right now you set the request header. If both doesn't solve the issue, verify if the server knows the correct hash. When the client sends the correct hash and the server has NULL as hash, then the validation will fail ofcourse.

3 Comments

The csrf code is in the cookies. But according to the JS code, it should be in the request headers, it is not. So I would say that the CSRF code is not sent to the server, that is what I suspected but I don't know why.
Did you verify the cookie 'csrftoken' is present?
It is present in the web page and in the cookies. But not in the HTTP header for the POST request. I updated my question with some additional info.
0

I solved the problem by calculating a value for the token whenever the DOM is ready, and AJAX reloads.

The javascript code uses the reload_ajax() function to handle the token, as well as any other things I need to refresh both on the initial page load, and subsequent AJAX calls.

<script type="text/javascript">

function reload_ajax() {
    // Useful on initial page load, and after calling AJAX.
    $("input#token").val("{{ csrf_token }}");
}

$(document).ready(function() {
    $("form#stats").unbind("submit");      // Prevents calling document-ready multiple times
    $("form#stats").submit(function(event) {
        event.preventDefault();
        $.ajax({
            type:"POST",
            url: $(this).attr("action"),
            data: $(this).serialize(),     // Serialize the form
            dataType: "json",
            success: function(response){
                $("#stats_ajax").html(response.html);  // Object to refresh after AJAX 
                reload_ajax();
            }
        });
        return false;
    });

    reload_ajax();
});
</script>

I'm using two HTML files, In main.html, I have the javascript code above, and the following:

<div id="stats_ajax">
    {% include "stats_ajax.html" %}
</div>

In stats_ajax.html, I have the actual form (plus other things I need to refresh)

<form id="stats" action="/main/" method="post">
    <!-- The value will be inserted when the DOM reloads. -->
    <input id="token" type="hidden" name="csrfmiddlewaretoken" value="" />
    <!-- Other input elements -->
    <button type="submit">Submit</button>
</form>

In my views.py file,

# Not all imports may be needed for this example
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
import json

def main(request):
    # This request should only be posting AJAX
    if request.is_ajax():
        data = {}
        data["message"] = "Awesome"
        # This is the template to load on the DOM object to update
        html = render_to_string("stats_ajax.html", data)
        res = {"html": html}
        return HttpResponse(json.dumps(res), mimetype='application/json')

    # Handle GET to this view
    return redirect("/main")

Finally, my urls.py has

# Replace "application_name" with your own value
url(r'^main', 'application_name.views.main'),

1 Comment

did everything as you, but error(( Forbidden (403) CSRF verification failed. Request aborted. "GET /main HTTP/1.1" 200 1111 "POST /main HTTP/1.1" 403 2294

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.