Piero Marini Piero Marini - 2 months ago 39
Javascript Question

How do i include the csrf_token to dropzone Post request (Django)

Allright this is resolved. Just editing in case anyone runs into the same problem.

Add the Code posted in Comment marked as answer in the same javascript file.
When defining

var myDropzone = new Dropzone(...
...//More stuff here
headers:{
'X-CSRFToken' : csrftoken
}


And thats it.

So im getting a 403 Forbidden when submitting the POST request through dropzone.js to django.Django displayed the message saying that I didnt include the CSRF token, but I don't know how to actually include it if im not using a form in the HTML.

document_form.html



{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}Add files{% endblock %}
{% block files %}
<div class="container-fluid" id="container-dropzone">
<div id="actions" class="row">

<div class="col-lg-7">
<span class="btn btn-success file-input-button">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
</span>
<button type="submit" class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
<button type="reset" class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel upload</span>
</button>
</div>

<div class="col-lg-5">
<!-- file processing state -->
<span class="fileupload-process">
<div id="total-progress" class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</span>
</div>
</div>

<div class="table table-striped files" id="previews">
<div id="template" class="file-row">
<div>
<span class="preview"><img data-dz-thumbnail></span>
</div>
<div>
<p class="name" data-dz-name></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<p class="size" data-dz-size></p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0"
aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%"
data-dz-uploadprogress>
</div>
</div>
</div>
<div>
<button class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start</span>
</button>
<button data-dz-remove class="btn btn-warning cancel">
<i class="glyphicon glyphicon-ban-circle"></i>
<span>Cancel</span>
</button>
<button data-dz-remove class="btn btn-danger delete">
<i class="glyphicon glyphicon-trash"></i>
<span>Delete</span>
</button>
</div>
</div> <!-- /table-striped -->
</div> <!-- /container-fluid -->
</div>
{% endblock %}
{% block dz-add %}
<script src="{% static 'js/dropzone-bootstrap.js' %}"></script>
{% endblock %}


dropzone-bootstrap.js



$(function() {
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);

var myDropzone = new Dropzone(document.querySelector("#container-dropzone") , {
url: "/dashby/files/add/", //url to make the request to.
thumbnailWidth: 80,
thumbnailHeight: 80,
parallelUploads: 20,
previewTemplate: previewTemplate,
autoQueue: false,
previewsContainer: "#previews",
clickable: ".file-input-button",
headers: { // Tried to apply the token this way but no success.
'X-CSRFToken': $('meta[name="token"]').attr('content')
}
});

myDropzone.on("addedfile", function(file){
file.previewElement.querySelector(".start").onclick = function(){
myDropzone.enqueueFile(file);
};
});

myDropzone.on("totaluploadprogress", function(progress){
document.querySelector("#total-progress .progress-bar").style.width = progress + "%";
});

myDropzone.on("sending", function(file){
// Show total progress on start and disable START button.
document.querySelector("#total-progress").style.opacity = "1";
file.previewElement.querySelector(".start").setAttribute("disabled", "disabled");
});

// Hide progress bar when complete.
myDropzone.on("queuecomplete", function(progress){
document.querySelector("#total-progress").style.opacity = "0";
});

// Setup buttons for every file.
document.querySelector("#actions .start").onclick = function(){
myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
};
document.querySelector("#actions .cancel").onclick = function(){
myDropzone.removeAllFiles(true);
};
});


In my base.html im adding all the required files (dropzone, jquery, bootstrap and my custom javascript file)

For the django form handling:

views.py



class DocumentCreate(CreateView):
model = Document
fields = ['file']
def form_valid(self, form):
self.object = form.save()
data = {'status': 'success'}
response = JSONResponse(data, mimetype =
response_mimetype(self.request))
return response


My "Document" model



class Document(models.Model):
file = models.FileField(upload_to = 'files/',
validators=[validate_file_type])
uploaded_at = models.DateTimeField(auto_now_add = True)
extension = models.CharField(max_length = 30, blank = True)
thumbnail = models.ImageField(blank = True, null = True)

def clean(self):
self.file.seek(0)
self.extension = self.file.name.split('/')[-1].split('.')[-1]
if self.extension == 'xlsx' or self.extension == 'xls':
self.thumbnail = 'xlsx.png'
elif self.extension == 'pptx' or self.extension == 'ppt':
self.thumbnail = 'pptx.png'
elif self.extension == 'docx' or self.extension == 'doc':
self.thumbnail = 'docx.png'

def delete(self, *args, **kwargs):
#delete file from /media/files
self.file.delete(save = False)
#call parent delete method.
super().delete(*args, **kwargs)

#Redirect to file list page.
def get_absolute_url(self):
return reverse('dashby-files:files')

def __str__(self):
#cut the 'files/'
return self.file.name.split('/')[-1]

class Meta():
#order by upload_date descending
#for bootstrap grid system. (start left side)
ordering = ['-uploaded_at']


I created a Json response to handle the dropzone.

response.py



from django.http import HttpResponse
import json

MIMEANY = '*/*'
MIMEJSON = 'application/json'
MIMETEXT = 'text/plain'

# Integrating Dropzone.js with Django.
def response_mimetype(request):
can_json = MIMEJSON in request.META['HTTP_ACCEPT']
can_json |= MIMEANY in request.META['HTTP_ACCEPT']
return MIMEJSON if can_json else MIMETEXT

# Custom HttpResponse
class JSONResponse(HttpResponse):
def __init__(self, obj='', json_opts=None, mimetype=MIMEJSON,
*args, **kwargs):
json_opts = json_opts if isinstance(json_opts, dict) else {}
content = json.dumps(obj, **json_opts)
super(JSONResponse, self).__init__(content, mimetype,
*args, **kwargs)


I've been stuck with this problem for a day now, so decided to ask for help here as I havn't been able to find one.

Thanks to anyone that takes the time to read and for any help/tips that I can get.

J F J F
Answer

Django’s docs have a reference for this:

While [a special parameter] can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many JavaScript frameworks provide hooks that allow headers to be set on every request.
[…]

Acquiring the token is straightforward:

// using jQuery
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

[…] Finally, you’ll have to actually set the header on your AJAX request, while protecting the CSRF token from being sent to other domains using settings.crossDomain in jQuery 1.5.1 and newer:

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

If you run these two code blocks before you start to make requests, it should Just Work™.

In summary, just use this code block:

// from https://docs.djangoproject.com/en/1.10/ref/csrf/ via http://stackoverflow.com/a/39776325/5244995.
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});