Ok, now you got dojango and you want to write an AJAX app. There are some basics that just need to be provided in order to make that a piece of cake. Dojango offers them. And this article will show you how to best plug those pieces together and get up and running with ajax and dojango.
This article will show you how to simply pass data properly from the backend to the client.
JSON communication with the server
There are enough use cases (and they are becoming more) when you want to get data from the server and the most common data format nowadays for that is JSON, for various good reasons.
Let’s construct a simple form who’s data we want to submit to the server and know if it all went ok or not. For now we just print the submitted data on the django console (from where you started the local dev server) and we let the user know about the submission with the small text “Submitted” beside the submit button.
So let’s get started with the JavaScript side. Dojango luckily provides us with the basic infrastructure, so let’s create a template that extends dojango/base.html (it is available since the settings.py includes the app “dojango”, see here for instructions), that provides us with everything we need to have dojo running even the doctype of the document and HTML headers, just in short a valid HTML file, we only have to worry about our code.
So let’s build the simple.html file in the templates folder of our django app (I called my app “core”).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | {% extends "dojango/base.html" %} {% block dojango_page_title %}Simple AJAX with dojango{% endblock %} {% block dojango_header_extra %} <script type="text/javascript"> function userFormSubmit(){ var form = dojo.byId("userForm"); dojo.xhrPost({url:form.action, handleAs: "json", content:{surname:form.surname.value, firstname:form.firstname.value }, load:function(response, ioArgs){ dojo.byId("info").innerHTML = "Submitted"; } }); } </script> {% endblock %} {% block dojango_content %} <form id="userForm" onsubmit="userFormSubmit(); return false;" action="/simple-ajax-set/"> First name: <input id="firstname" /><br /> Surname: <input id="surname" /><br /> <input type="submit" value="Submit" /> <span id="info"></span> </form> {% endblock %} |
As you can see we are overriding a couple of blocks, that are defined in the dojango/base.html. The first one (line 3) is just for setting the page title, the second one (line 5) is for putting our JavaScript code inside html-head. And “dojango_content” (line 21) is finally the content of the body node.
We also have to wire up the template to render when we access http://localhost:8000/simple/ and the AJAX method, that we submit the data to, like so in the urls.py:
1 2 3 4 5 6 7 8 | from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^dojango/', include('dojango.urls')), (r'^simple/', 'core.views.simple'), (r'^simple-ajax-set/', 'core.views.simple_ajax_set'), ) |
We just added the two last lines that map our URLs (line 6 and 7) to the view functions.
Our views.py just needs to implement these two functions, like this.
1 2 3 4 5 6 7 8 9 10 11 12 | from django.shortcuts import render_to_response from dojango.decorators import json_response def simple(request): return render_to_response('simple.html') @json_response def simple_ajax_set(request): firstname = request.POST['firstname'] surname = request.POST['surname'] print firstname, surname return {'success':True} |
The function “simple” (line 4) uses the standard django way to render a view and return the content to the client. It finds the template simple.html in the app’s template path.
The function “simple_ajax_set” (line 8) will receive our form data and (for now) only print them. You can see the first speciality here, it uses the decorator “json_response” that we imported from dojango.decorators. This decorator takes care of returning proper JSON data, all the data you stuff in there are returned to the client JSON encoded, it basically is json_encode as described here. If you have AJAX calls and want to return JSON data, this is the easiest way to do it and it comes with dojango. It is a special (though very simple) implementation that has not made it’s way into the django core because it is really just AJAX-focused, while the django serializer is django model focused. Additionally this implementation solves a couple of problems and adds some features that are very useful when working with AJAX data, for more explainations see this article.
Handle the return
Now we can submit the form and see that the data got submitted. We don’t know yet if the data had been successfully handled on the server. So let’s add proper error handling.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function userFormSubmit(){ var form = dojo.byId("userForm"); dojo.xhrPost({url:form.action, handleAs:"json", content:{surname:form.surname.value, firstname:form.firstname.value }, load:function(response, ioArgs){ if (response.success){ dojo.byId("info").innerHTML = "Submitted successfully"; } else { dojo.byId("info").innerHTML = "Error: "+response.error; } }, error:function(data){ // This happens on a 500 error or alikes. dojo.byId("info").innerHTML = "Error sending data."; } }); } |
If you looked into the return data (i.e. via FireBug) you might have seen that
1 | {"success":true} |
was returned. But until now this was passed into the load function (line 8) as a string, but we want them as JSON. So we have to tell the xhrPost() call to handle it as JSON (line 4). Now we can also evaluate the success value easily as shown in line 9. If the server returns
1 | success |
with the value
1 | false |
we know something went wrong on the server. Let’s implement on the server that the surname has to have at least three characters. In the case of an error the server returns the additional property “error” which we then show to the user (line 12) to inform him about the error.
The function in line 15 handles all kind of connection and/or submission errors that may occur in a lower level. Make sure to not leave your users in the dark about this.
Let’s look at the server code for that:
1 2 3 4 5 6 7 8 9 10 11 12 13 | @expect_post_request @json_response def simple_ajax_set(request): ret = {} firstname = request.POST['firstname'] surname = request.POST['surname'] if len(surname)<3: ret['error'] = 'Surname is too short.' ret['success'] = False if ret['success']: # Store the data here pass return ret |
We extended the function a little bit to do some simple error checking. You can see that we add the key “error” to the ret dict, which is then passed to the client if the surname was shorter than three characters. The JSON string returned in this case is
1 | {"success": false, "error": "Surname is too short."} |
.
Note that we didn’t explicitly set the value for
1 | success |
to
1 | True |
, the decorator
1 | json_response |
handles that for us. If no exception is thrown it assumes that the function went ok and it can return
1 | success=True |
. This comes in very handy, especially when you just have simple one task AJAX functions that i.e. just delete an item, you don’t have to return explicitly that the deletion went well,
1 | json_response |
does that for you if you throw no exception. We just made it the default behavior since it was right for most of our use cases.
Comments
I am wondering why your ‘dojo.xhrPost’ example uses the ‘content’ attribute and then specifically extracts the form’s two fields, instead of using the ‘form’ attribute and passing in the form object.
Perhaps to show explicitly how to pass arbitrary data to the server.
A mention of being able to say:
” form: form,”
instead of:
” content : {surname:form.surname.value, firstname:form.firstname.value },”
to show an even simpler way of doing it, if the data you are posting strictly conforms to the form itself. Especially as your forms may be a number of fields. Having to enumerate them all the time can be error prone.
(cf: http://api.dojotoolkit.org/jsdoc/dojo/HEAD/dojo.rawXhrPost )
July 29, 2008 — 12:39 am
Scanner
I created a function that collects the form’s elements:
…
function createContent(form){
var elements = {};
for(var i=0; i<form.elements.length; i++) {
elements[form.elements[i].id] = form.elements[i].value;
}
return elements;
}
…
content:createContent(form),
…
December 21, 2008 — 11:43 pm
RedWins
Error sending data.
February 22, 2009 — 12:50 pm
skyl
On Python 2.5.1, Django 1.1 beta (and I’m not at all sure if these versions matter!), the above example works perfectly once you change:
if ret['success']:
to
if ‘success’ in ret.keys():
if ret['success']:
…which I believe satisfies the intent of the original code. The original expression will throw a KeyError if ret['success'] wasn’t set. This will in turn cause a 500 and the “error sending data” message on the page. This might be the cause of the problem Skyl encountered.
April 7, 2009 — 07:22 pm
Ali Hussain
Oops, indentation got whacked in my previous post. The second ‘if’ needs to be indented.
April 7, 2009 — 07:22 pm
Ali Hussain
Hi,
first of all thanks for such a helpful and great article. but one thing when i tried to run the code script with this line “@expect_post_request” i am getting error:
NameError at /simple/
name ‘expect_post_request’ is not definedRequest Method: GET
Request URL: http://localhost:8000/simple/
Exception Type: NameError
Exception Value: name ‘expect_post_request’ is not defined
Exception Location: D:Django Applicationsmydojangositemyappviews.py in , line 16
Please tell me what can i do to resolve it.
Thanks
November 6, 2009 — 01:01 pm
saurabh dwivedi
re: Sauradh
Same problem. Fixed it:
from dojango.decorators import json_response,expect_post_request
expect_post_request is a method on dojango.decorators, just like json_response.
November 10, 2009 — 07:13 pm
Sean
This good summary assited me very much! Bookmarked the website, extremely great categories everywhere that I see here! I really appreciate the information, thank you.
February 16, 2011 — 04:35 am
Adan Creamer
Is there a difference between Dojango and Dajax? The mechanics seem pretty much the same.
June 2, 2011 — 02:44 pm
Chris
Actually dojango is focused on dojo integration with django, not any ajax library, there is more deep rooted connections between dojo and django in dojango simply because dojango only wants to integrate dojo tighter with django not any ajax library. which is the benefit and its limit at the same time
Wolfram
June 2, 2011 — 09:50 pm
Wolfram Kriesing
Hello,
I always get “Error sending data.” and I don’t know why. Could somebody help me?
Christian
July 6, 2011 — 02:28 pm
Christian Herz
I got Problems with csrf
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
and i don’t know a solution
July 6, 2011 — 06:45 pm
Christian Herz