Thursday, April 22, 2010

Django + Dojo

When I work on HTML projects, I usually use the Dojo Toolkit for my Javascript needs. Lately I've been spending some time playing around with the Python web framework Django. I did some internet searching and found Dojango, a project that integrates Django with Dojo. Dojango has features for automatically turning Django form fields into Dijits (Dojo UI widgets), but unfortunately Dojango uses Dojo's custom HTML attributes with Dojo's parseOnLoad option. I prefer to create Dijits programatically so that my markup stays clean. I decided to develop a Django app to meet my needs.

Code is available from SVN. Instructions are below.

Instructions:

Settings

# Setup app in settings.py

# Required attributes:

# The URL to get dojo.js from
DOJO_URL = MEDIA_URL + 'js/dojo'

# Optional attributes:

# Set to True to add Dojo setup to template
DOJO_ENABLED = True

# Set Dojo theme
DOJO_THEME = 'tundra'

# Set the value of djconfig
DOJO_DJCONFIG = {'isDebug': False, 'parseOnLoad': False,
'modulePaths': {'app': MEDIA_URL + 'js/app'}}

# More on this later
DOJO_FORM_FUNCTION = None

# Attach middleware
MIDDLEWARE_CLASSES = (
'dojo.middleware.DojoMiddleware',
...
)



The dojo object

The middleware attaches a Dojo object to each request. You can access the object from your views, and use it to set Dojo parameters.

# The dojo object has several useful attributes and methods.

# The path to dojo.js (from settings.DOJO_URL), read-only
request.dojo.src

# Theme
request.dojo.theme = 'soria'

# DjConfig
request.dojo.dj_config['isDebug'] = True

# Convenience method to set module paths in dj_config
request.dojo.set_module_path('custom', 'url_to_custom_module')

# Add stylesheets
request.dojo.append_stylesheet('url_to_custom_stylesheet')

# Require modules
request.dojo.append_module('module.to.require')

# Set function to addOnLoad
request.dojo.append_aol('function() {do_something();}')

# Set function to addOnLoad before other already set
request.dojo.prepend_aol('function() {do_something();}'



Forms

Django forms can easily be 'dijitized'.

from dojo import models as dojo

class Register(forms.Form):
username = forms.RegexField(
label='Choose a username (letters and numbers only)',
min_length=2,
max_length=16,
regex=r'^[\w]{2,16}$',
error_messages={
'invalid': 'Username must be 16 characters or shorter, and can only'
' contain letters, numbers, underscores and dashes.'
}
)

# Use the dojo_field function to attach dijit
# parameters to a Django form field.
dojo.dojo_field(username, 'dijit.form.ValidationTextBox')



"""
dojo_field arguments

required
==========
* field - Django field to attach dijit parameters to.
* dojo_type - str, the qualified name of the dijit class to use

keyword
=========
* attr_map - dict, Used to map Django field parameters to Dijit parameters.
Overrides the default values in dojo.models.default_attr_map.
The dict elements should be structured as follows:

Key == Django attribute name
Value == tuple with elements:
[0] == Dijit attribute name to map to
[1] == None, or callable to convert Django value to Dijit value

EXAMPLE:
{
'max_length': ('maxLength', None),
'regex': ('regExp', lambda a: '%s' % a.pattern)
}
* dojo_attrs - dict, Attributes will be applied directly to dijit.
Key == dojo attribute name
Value == dojo attribute value
"""
}


After creating a form, it must be instrumented to create the Javascript required to create the dijits.



def my_view(request):

my_form = Register()

# This call generates all the necessary Javascript code
request.dojo.dojo_form(my_form)

# By default, the function code generated
# is a string to be added in-line.
#
# If you prefer to call a pre-defined JS function,
# just set the request.dojo.form_function attribute.
#
# The value of the attribute should be a tuple where:
# [0] == qualified Dojo module name where function exists
# [1] == function name
#
# request.dojo.form_function can also be set automatically
# by setting DOJO_FORM_FUNCTION in settings.py


Template

Include the following tags within the 'head' tag of your HTML template:


{% load dojo %}
{% dojo request.dojo %}


The Dojo app also includes a script for creating a Dojo build:


python manage.py dojo_build