Wandering Thoughts archives


How I'm doing AJAX with Django in my web app

Django has native AJAX support for serializing model objects and query results to and from JSON (or XML); various people have then added various featureful packages on top of this. For my web app all of this was overkill and too complex. What I needed for my limited client side form validation was a simple AJAX endpoint service, one that reported whether or not a potential login name was acceptable by returning a JSON dictionary with both a validity flag and a message to display to the user about the situation.

(I decided that I wanted to explicitly mark whether the login was valid or not rather than have client side code try to infer it from, say, whether or not there was a message. The latter seemed excessively fragile when being explicit was simple and cheap.)

So here's what I did (with the disclaimer that this may horrify experienced Django developers). First off, for these form fields you're going to want to pull as much of the field validation logic as possible out of check_<field> functions in your regular form classes and into reusable functions somewhere. Fortunately I was already mostly doing this because I had several forms that all had a login field that they had to validate.

(I couldn't extract all of the logic because I'd made the login field a RegexField with a maximum length. This may have been a mistake; automatic field validation is very convenient until it isn't.)

The main validation endpoint is exposed as a REST-style URL that looks like '/check/login/<proposed login>'. This is connected to a plain Django view function with a single parameter, the login. The view function duplicates the RegexField's regular expression check then if that passes calls my general login field validation function, sets up a dictionary based on the result, turns it into JSON with simplejson.dumps() (simplejson is from django.utils), and finally returns that as the HTTP response (with the content type set appropriately). The client side code picks it up from there and does appropriate client side magic.

(Jquery normally wants to form parameterized JSON URLs using a query parameter, ie something like '/check/login/?<proposed login>'. Since I didn't know how to handle that in Django and didn't want to work it out, I punted and forced a more REST-ish URL with some hackery. I actually like the REST-ish URL better than the Jquery one.)

This left me with a small problem: how was the client side JavaScript code supposed to know the (base) URL for the validation endpoint? My solution is a hack. First, I created a second 'endpoint' view that is attached to the plain '/check/login/' URL (without a login parameter). This view does nothing, because its only purpose in life is being a valid argument for the url tag in templates. Then in the HTML template for the form page I added a JavaScript snippet that just had:

var loginBaseUrl = "{% url requests.views.checklogin_url %}";

The actual JavaScript code (which comes from a separate file at a separate URL) then uses loginBaseUrl instead of trying to hard-code the URL of the endpoint. The advantage of this hack is that the endpoint URL is automatically adjusted for whatever environment the Django app is running in (production, my testing, whatever) without my file of actual JavaScript code having to be run through Django template processing or any other form of (pre)processing.

(If you had a lot of endpoints you could come up with a more elaborate JavaScript data structure to hold this information and maybe a more elaborate scheme for propagating it around. Or at least something that doesn't involve random global JavaScript variables stuffed into your page HTML. However, this was a quick hack and it works, and I'm new to JavaScript so I get to do things the crude and direct way for at least a bit.)

PS: both my JavaScript code and our environment are so small that I'm not bothering to do any minimization, bundling, or the like of the code right now. In a larger environment I imagine that such 'ready it for deployment' processing of the JavaScript would be the natural place to insert endpoint URLs and the like.

python/MyDjangoAjax written at 00:29:07; Add Comment

Page tools: See As Normal.
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.