Making Django and Javascript work nicely together
Backend and frontend cooperation may not be an easy task. For example in javascript code you may need some URLs to views or to icons required for some widgets. You can hardcode it or cleverly pass required data from backend to frontend. Also AJAX requests often want batches of data in JSON format. There is a way to make it quickly and clean. In this article I'll show some Django applications and solutions for much easier backend - frontend cooperation.
django-javascript-settings
django-javascript-settings is a Django application that can pass variables from backend to JavaScript. You can install it with:Next add 'javascript_settings', to INSTALLED_APPS.
In your main HTML template add:{% load javascript_settings_tags %}
<script type="text/javascript">{% javascript_settings %}</script>
<script type="text/javascript">var configuration = {};</script>
We got a "configuration" variable with an empty array.
To add something to that array you need to define a javascript_settings function in urls.py of your django application. This function has to return a dictionary:def javascript_settings():
js_conf = {
'page_title': 'Home',
}
return js_conf
<script type="text/javascript">var configuration = {"jsdemo": {"page_title": "Home"}};</script>
django-javascript-settings can be used to pass view URLs (using reverse function), urls to needed graphics, or rendered templates and so on.
AJAX Requests
Making AJAX requests with jQuery is very easy. For this example I created a main page template:{% load javascript_settings_tags %}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">{% javascript_settings %}</script>
<script src="{{ STATIC_URL }}scripts.js"></script>
</head>
<body>
<h1>Demo</h1>
</body>
</html>
In the "static" folder of my application I created scripts.js file in which I'll write some jQuery code that will make an AJAX request to a given view.
The views look like so:from django.views.generic import TemplateView, View
from django.http import HttpResponse
class HomeView(TemplateView):
template_name = "home.html"
home = HomeView.as_view()
class AjaxView(View):
def get(self, request, **kwargs):
return HttpResponse('ok')
ajax_view = AjaxView.as_view()
from django.conf.urls import patterns, url
from django.core.urlresolvers import reverse
urlpatterns = patterns('jsdemo.views',
url(r'^ajax/$', 'ajax_view', name='ajax-view'),
)
def javascript_settings():
js_conf = {
'ajax_view': reverse('ajax-view'),
}
return js_conf
url(r'^demo/', include('jsdemo.urls')),
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "GET",
success: function(data){
}
});
});
This covers all the elements. The request should now work and you can check it in Firebug or other browser developers console. The request should return "ok" from the view.
This simple code makes a GET request. Quite often POST requests are needed - like when some data is sent. jQuery part is very easy:
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "POST",
data: {"key": "value"},
success: function(data){
}
});
});
In "AjaxView" we change "get" to "post" and reload the page. The AJAX request should fail - 403, "CSRF verification failed. Request aborted.". We made a POST request without the csrf token (what we put in forms). You could use csrf_exempt on the view but that isn't the best solution for this problem.
In csrf Django documentation we can find a solution. We have to set X-CSRFToken header with the csrf cookie value. Using jQuery Cookie it will be easy:
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "POST",
data: {"key": "value"},
success: function(data){
},
beforeSend: function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
});
});
We have a fully working AJAX request. Now it's time to use JSON to get a batch of data from the view.
Just import "json" module and modify the view to:class AjaxView(View):
def post(self, request, **kwargs):
data = {'user_id': self.request.user.id,
'name': self.request.user.username}
data = json.dumps(data)
return HttpResponse(data)
ajax_view = AjaxView.as_view()
Django-annoying
django-annoying is a set of decorators and helpers shortening some common tasks. We can use the ajax_request decorator to improve our view. If we use this decorator on a view it has to return a dictionary. The decorator will encode it to JSON and will return a JsonResponse (like HttpResponse but with correct JSON mimetype).
We can instal django-annoying the usuall way:from annoying.decorators import ajax_request
from django.views.generic import TemplateView, View
class HomeView(TemplateView):
template_name = "home.html"
home = HomeView.as_view()
class AjaxView(View):
def post(self, request, **kwargs):
return {'user_id': self.request.user.id,
'name': self.request.user.username}
ajax_view = ajax_request(AjaxView.as_view())
Comment article