June 10th, 2012

Django and AJAX: Dajax

Tutorial for: Dajax

Requirements:

Dajax enables you to build web applications using only Python code and HTML, with little to no JavaScript required.

This is the third and final tutorial in the Django and AJAX set. This tutorial will focus on building a simple application which uses Dajax to load data from a model and update data back into a model. This tutorial will use some JavaScript, mainly to build the request required when updating or querying objects. For an introduction on what AJAX is and how it works, please refer to the first tutorial in this set.

In order to begin using Dajax, you will need to install Dajaxice. Here is what needs to be added to your settings.py to make Dajax work:

DAJAXICE_MEDIA_PREFIX="dajaxice"
....
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
    'django.template.loaders.eggs.Loader',
)
...
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'dajaxice',
    'dajax',
    .....
)

You can change the DAJAXICE_MEDIA_PREFIX to anything you desire, it is used as the virtual directory on your website which serves the AJAX-related views. Next, you need to modify your urls.py:

from django.conf.urls.defaults import patterns, include
from dajaxice.core import dajaxice_autodiscover
from django.conf import settings

dajaxice_autodiscover()

urlpatterns = patterns('',
    ...
    (r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')),
    ...
)

This will use the prefix configured in your settings.py to tell Dajaxice where the views will be loaded from. Next up, we need to modify our base template to tell the browser where to load the Dajaxice and Dajax JavaScript from. Since Dajax is more complex than Dajaxice, there are additional JavaScript files which need to be loaded:

{% load dajaxice_templatetags %}
...
<head>
...
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="jquery.ba-serializeobject.min.js"></script>
<script type="text/javascript" src="jquery.dajax.core.js"></script>
{% dajaxice_js_import %}
...
</head>

This tutorial will be using jquery.ba-serializeobject.js again, so go ahead and download it if you do not have it, and place it into your static directory. You will also notice jquery.dajax.core.js above. This file does all the magic on the client side, and is required for Dajax to work. That being said, Dajax also supports other popular JavaScript frameworks such as Prototype, Dojo, and Mootools. This will allow you to use Dajax in almost any existing environment. Given that you are using Django to power the backend that is.

That's really it for configuration, so lets do a simple test to confirm that you have Dajax all up and running in your Django project. Create a file called ajax.py in your applications folder, not the project, and enter in the following:

from dajaxice.decorators import dajaxice_register
from dajax.core import Dajax

@dajaxice_register
def say_hello(req):
    dajax = Dajax()
    dajax.alert("Hello World!")
    return dajax.json()

A very simple Hello World message. This is just about the most simplest Dajax callback you can build. Well, you could remove the alert and return nothing, but what's the point of a callback that simple? Here's a small bit of a code to insert into your template's body tag to test the callback out:

<script type="text/javascript">
Dajaxice.dajaxapp.say_hello(Dajax.process);
</script>

Here our application name is dajaxapp, change this to the label of your application's package. This should be for the most part very easy to read and understand code. Dajax.process is the data processor for the returning data, and this JavaScript function manages the part of taking what we do in Python and making it work in JavaScript. Here is what the returned data looks like, if you are curious:

[{"cmd": "alert", "val": "Hello World!"}]

Dajax takes what you did in Python, and turns it into something which JavaScript can parse and do something with. In other examples, I will also provide the response from Dajax, so that you can further understand what is happening under the hood.

Now that the most simplest example is out of the way, lets build a model and use that with Dajax to fetch data and display it to the end-user. First we need some sort of model:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=80)
    birthday = models.DateField()
    gift_bought = models.BooleanField()
    def __unicode__(self):
        return u"%s" % self.name

This is a very simple birthday reminder model, which keeps track of people, their important days, and of course if you already bought their gift. Nothing too special. Go ahead and populate the model with some data using either a Python shell or the admin interface.

First we'll put together the Dajax callback which will use the query and grab the data from the database. It will then assign the data to HTML IDs. We will also perform some formatting within Python to make the output look prettier:

from dajaxice.decorators import dajaxice_register
from dajax.core import Dajax
from ajaxsite.dajaxapp.models import Person

@dajaxice_register
def get_person(req, pk):
    dajax = Dajax()
    p = Person.objects.get(pk=pk)
    dajax.assign('#idName', 'innerHTML', p.name)
    dajax.assign('#idDay', 'innerHTML', p.birthday.strftime("%B %d"))
    gift = 'Yes' if p.gift_bought else 'No'
    dajax.assign('#idGift', 'innerHTML', gift)
    return dajax.json()

This Dajax callback will accept one argument, which is the primary key of the Person object. Here is the HTML code to make this all work:

<body>
<script type="text/javascript">
Dajaxice.dajaxapp.get_person(Dajax.process, {'pk':1});
</script>
Name: <span id="idName"></span><br/>
Birthday: <span id="idDay"></span><br/>
Gift bought: <span id="idGift"></span><br/>
</body>

Fairly straightforward. Here is the response body of the AJAX request:

[
  {"cmd": "as", "id": "#idName", "val": "John Smith", "prop": "innerHTML"},
  {"cmd": "as", "id": "#idDay", "val": "October 19", "prop": "innerHTML"},
  {"cmd": "as", "id": "#idGift", "val": "No", "prop": "innerHTML"}
]

You can see above all the assignment calls being performed. Using Dajax, you can build web applications fairly quickly; and easily add new JavaScript functions to use. The best part about using Dajax over other solutions, is that you only need to create a Python function, there is no need to do anything in JavaScript. Once the Python function is complete, you can go ahead and use it directly in your HTML page to load data and do other various tasks. Let's go a bit further in this example, and make the data easily browsable by using 2 simple buttons. This will allow end-users to easily browse the data in the database, either based on a particular filter, or all the data. This will expand on the get_person Dajax callback and enable it to control a simple browsing widget. This time, we'll begin with the HTML code, as that has changed a large amount:

<style>
.hideIt { display: none; }
</style>
</head><body>
<script type="text/javascript">
var cur = 1;
Dajaxice.dajaxapp.get_person(Dajax.process, {'pk':cur});
</script>
<div id="idError" class="hideIt">You have reached the end of the database.</div>
<table bgcolor="#acacac" border="1">
<tr><th>Name</th><td id="idName"></td></tr>
<tr><th>Birthday</th><td id="idDay"></td></tr>
<tr><th>Gift bought</th><td id="idGift"></td></tr>
</table>
<a href="#" onclick="Dajaxice.dajaxapp.get_person(Dajax.process, {'pk':cur-1});">&lt;</a> <a href="#" onclick="Dajaxice.dajaxapp.get_person(Dajax.process, {'pk':cur+1});">&gt;</a>
</body>

This example uses a little more JavaScript, it is used to keep track of the application's current state. In this case, it is keeping track of the current pk being loaded from the database. The buttons merely alter this variable when requesting an update from the server. Here is the updated ajax.py to make this widget work:

from dajaxice.decorators import dajaxice_register
from dajax.core import Dajax
from ajaxsite.dajaxapp.models import Person

@dajaxice_register
def get_person(req, pk):
    dajax = Dajax()
    try:
        p = Person.objects.get(pk=pk)
    except Person.DoesNotExist:
        dajax.remove_css_class('#idError', 'hideIt')
        return dajax.json()
    dajax.add_css_class('#idError', 'hideIt')
    dajax.assign('#idName', 'innerHTML', p.name)
    dajax.assign('#idDay', 'innerHTML', p.birthday.strftime("%B %d"))
    gift = 'Yes' if p.gift_bought else 'No'
    dajax.assign('#idGift', 'innerHTML', gift)
    dajax.script("cur = %d;" % pk)
    return dajax.json()

The server code will be controlling the class for the idError. This will allow the application to alert the end-user that they have reached the end or beginning of the dataset. This code will obviously not work too well in a production environment, due to the fact that when a Person is deleted, it will cause the error to be displayed. However, for simplicities sake, this example will not go through all those checks, and will avoid using a queryset to limit the results. In this example, you will notice a few new Dajax commands being used, two of which relate to CSS alterations, and the other one runs some JavaScript on the client side. This JavaScript is used to update the state of the application in the browser. This could have been done on the client side directly, but in case of any errors, we don't want this variable to be incorrect or out of sync. Setting it this way, will ensure that the various will always be what you expect it to be.

The next obvious addition, is the ability to tell the application that a birthday gift has indeed been purchased for this specific person. We will implement this in the form of making the No clickable. If the user clicks it, it will signal to the application that the record needs to be updated to reflect that change. This change only requires modifying the ajax.py file:

from dajaxice.decorators import dajaxice_register
from dajax.core import Dajax
from ajaxsite.dajaxapp.models import Person

def _get_person(p):
    dajax = Dajax()
    dajax.add_css_class('#idError', 'hideIt')
    dajax.assign('#idName', 'innerHTML', p.name)
    dajax.assign('#idDay', 'innerHTML', p.birthday.strftime("%B %d"))
    gift = 'Yes' if p.gift_bought else '<a href="#" onclick="Dajaxice.dajaxapp.bought_gift(Dajax.process, {\'pk\':cur});">No</a>'
    dajax.assign('#idGift', 'innerHTML', gift)
    dajax.script("cur = %d;" % p.pk)
    return dajax.json()

@dajaxice_register
def get_person(req, pk):
    try:
        p = Person.objects.get(pk=pk)
    except Person.DoesNotExist:
        dajax = Dajax()
        dajax.remove_css_class('#idError', 'hideIt')
        return dajax.json()
    return _get_person(p)

@dajaxice_register
def bought_gift(req, pk):
    p = Person.objects.get(pk=pk)
    p.gift_bought = True
    p.save()
    return _get_person(p)

Here I decided to separate the functions a bit. This will allow future functions to just return _get_person with the Person object. We will be using this new function in the next example. The next example will be a create form, this will allow us to add a new Person. I will not be building an edit form in this example, as people rarely, if ever change their birthday... However, this example should be enough to allow you to understand form development and create such a callback yourself:

from ajaxsite.dajaxapp.forms import PersonForm
....
@dajaxice_register
def add_person(req, form):
    f = PersonForm(form)
    if f.is_valid():
        return _get_person(f.save())
    dajax = Dajax()
    dajax.assign('#person_errors', 'innerHTML', 'Correct the following fields: %s' % f.errors)
    return dajax.json()

The rest of ajax.py is the same, also you will need to create a ModelForm for the Person model. Here are the new additions to the HTML code:

...
<script type="text/javascript">
var cur = 1;
function add_person(){
  data = $('#person_form').serializeObject();
  Dajaxice.dajaxapp.add_person(Dajax.process, {'form':data});
  return false;
}
Dajaxice.dajaxapp.get_person(Dajax.process, {'pk':cur});
</script>
...
<div id="person_errors"></div>
<form action="" method="post" id="person_form" accept-charset="utf-8">{% csrf_token %}
<table>{{form}}</table>
<input type="button" value="Add Person" onclick="add_person();" />
</form>

There you have it, a complete Dajax example, which shows how to build a simple dataset browser, and how to add new functions with very little work involved. The largest selling point of using Dajax is that once the initial configuration is done, adding new AJAX callbacks takes almost no time at all, and is mostly done through Python. Be sure to read through both the Dajaxice and Dajax documentation, they explain some very important deployment configurations.

This concludes the Django and AJAX tutorial set. I may create additional tutorials on Dajax, but it will not be part of this tutorial set and will focus on more advanced usage scenarios.

Dec. 12, 2012, 12:56 p.m. - Leandro Falanga

Hi, Nice tutorial, very clear. I followed many times, step by step, but I keep getting this error: Invalid block tag: 'dajaxice_js_import' Looks like template tag is not available, but I'm sure that everything it was done as you said. Maybe some path, url. Where should I start looking? Please help me! I would appreciate it.

Dec. 24, 2012, 4:56 p.m. - Kevin Veroneau

Leandro, the error you are receiving is due to not having the dajaxice in your Django's INSTALLED_APPS, or you did not install Dajaxice correctly. Dajax does require Dajaxice.

Jan. 25, 2013, 9:10 a.m. - Adolfo Casari

Hi, very good tutorial. One question: _get_person(p) is called with p being a Person model and also a Person form ( in return _get_person(f.save()) ) ...is this posiible in django, i.e., a model object and a form objetc from that model can be treated as the same type?

Jan. 25, 2013, 9:19 a.m. - Kevin Veroneau

Hello Adolfo. Thank you for your comment, and I am sorry about the issues you experienced while posting the comment. Python isn't statically typed, meaning you can pass any object as a parameter. Since this was an example, I did not do any checks to confirm the incoming object was what I am expecting, so if another programming uses this function and gives it a object that it cannot process, it will generate an exception. It is a good habit in dynamically typed languages to use "Assert" or do extensive exception handling to avoid any application errors. If you are the sole coding of your code, and know for sure what a function accepts, you are free to do as you please when passing around objects. Both the Form and Model objects in Django have similar enough properties that I can pass either or into this function and it will work without complaint. Before creating a function like this one, read into the Django docs to make sure it will work with both object types you plan on accepting or use "isinstance" to route the object around the function as needed.

Feb. 10, 2013, 6:51 a.m. - Fergus

Hi Kevin, I worked through your tutorials here and managed to get all of your examples working fine. I'm now trying to submit a form and save an object from within ajax.py, but can't seem to get it working. What I'd like to be able to do is to pass additional objects to the ajax function, and use both them and the form cleaned data to create a new object within ajax.py. It seems that this sort of thing should be relatively simple, but in the error log I get nothing except: 'Dajaxice: Something went wrong', which is not very helpful. Is there any change you would be willing to expand on this tutorial by doing something similar to my aims? It seems like it must be a fairly common use case. If not, do you have any suggestions as to how I could debug my code? Many thanks, I've found this set of tutorials extremely useful.

Feb. 14, 2013, 10:31 a.m. - Continue Reading

Good post but I was wondering if you could write a litte more on this topic? I'd be very grateful if you could elaborate a little bit further. Cheers!|

About Me

My Photo
Names Kevin, hugely into UNIX technologies, not just Linux. I've dabbled with the demons, played with the Sun, and now with the Penguins.




Kevin Veroneau Consulting Services
Do you require the services of a Django contractor? Do you need both a website and hosting services? Perhaps I can help.

This Month

If you like what you read, please consider donating to help with hosting costs, and to fund future books to review.

Python Powered | © 2012-2014 Kevin Veroneau