Wednesday, June 21, 2006

AGAD!! (Tutorial-ish sort of post)

Please note this blog has now moved Here.

Asynchronous Google (Web Toolkit) and Django

Personally, I think that the acronymn is way more fun to say than "Ajax". Picture this, you're sneaking up behind someone at a party... you wait until the perfect moment when the music has just paused slightly between songs and you yell one of two things, you yell "AJAX!" or "AGAD!" Also, it should be noted, that the person you chose to sneak up behind has been horribly disfigured by a childhood accident involving large quantities of a brand-name cleaner. NOW, which are you going to yell? At this point, the superiority of AGAD should be evident, but just in case not, I will proceed to actually talk about it.

The Google Web Toolkit (GWT) is designed to work primarily with a Java backend, but a lot of people, myself included, don't really want to mess with the monolithic terror that is a Java backend, and so Google provides a JSON interface as well. In my most recent application, I decided to use JSON to communicate between the GWT front end and Django backend. The results are midly thrilling, so it is not advised that you not follow the link unless you have a strong heart, a good back, and at least one friend named Bubbles for support.

www.lunchtimevoter.com

So, how do you integrate the Django and the GWT? The key part is found in the here. It's a Python-JSON package that will help Django to speak GWT's language. It does a pretty good job of converting standard Pythonisms into JSON. I actually use a modified version, which can be found here. It takes the Python-JSON package and adds the function of interpreting any Python object into JSON as well. Feel free to download and use to your hearts content...

Now, once you have got that, you need to create the object to send, say an object looking something like this, which is the code for a single choice in the lunchtime voter :

class DisplayChoice(object):
__name__ = "DisplayChoice"

def __init__(self,choice):
votes = LunchVote.objects.filter( choice__choiceName__exact = choice.choiceName )
blackballs = LunchBlackball.objects.filter( choice__choiceName__exact = choice.choiceName )

self.choiceName = choice.choiceName;

self.voteList = list()
self.blackballList = list()

for vote in votes:
self.voteList.append(vote.user.displayName)

for blackball in blackballs:
self.blackballList.append(blackball.user.displayName)

As you can see, this code is nothing more than a shell for containing a few essentials that need to be communicated to the frontend. It is then written using the JSON write function with a call such as:

response = HttpResponse(write(displayChoice))
response['Pragma'] = "no cache"
response['Cache-Control'] = "no-cache, must-revalidate"
return response;

Since you'll be updating the AJAX frequently (that's kinda the point of the AJAX stuff isn't it?) you will want to the cache control lines to make sure you get a new copy each time you ask for it. Additionally, you can add some random garbage GET variable to your request from GWT to force the browser to recognize the response as new. After that, you have to decode the thing in your GWT application. This is actually really easy, and for the most part, you can just follow along with the included JSON example. However, it can be a bit tricky to get the JSON decoder working in *your* application. After copying the JSON decoder into your own project structure, it is probably easiest to leave the decoder in its own package and import it using the XML files. Firstly, you want to remove the entry-point from the JSON xml file, just rip the whole line out of there. Secondly, you want to inherit the json classes. As an example, here is what the core xml file for the lunchtime voter looks like:

<module>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.sample.json.JSON'/>

<!-- Specify the app entry point class. -->
<entry-point class='com.lunchtimevoter.client.Display'/>
</module>

And that's about it. After that, it's all standard django and or gwt stuff, so experiment and have fun :)