Dugan Chen's Homepage

Various Things

Elegantly Adding HTML5 Validation Attributes to Django Form Fields

The current version of Django, 1.4, does not use HTML5’s Web Forms 2.0 attributes. Later versions probably will, but here’s an elegant way to add them in the meantime.

What you probably don’t want to do is this:

file_field = FileField(widget=ClearableFileInput(
    attrs={'required': 'required'}))
text_field = CharField(widget=TextInput(
    attrs={'required': 'required'}))

That rolls back your reasons for choosing Python and Django.

It turns out, however, that that creating widgets and setting their HTML attributes can be done in two phases:

text_field = CharField()
text_field.widget.attrs['required'] = 'required'

You can then use the extract method refactoring to create a function that adds these attributes to new fields:

def html5_required(field):
    if field.required != False:
        field.widget.attrs['required'] = 'required'
    return field

Notice that the html5_required function returns the field passed to it. Notice also that that each Django field is callable, as are all classes with __init__ methods. We can therefore use a technique, called function composition and common in languages such as Haskell, to combine them.

Neither Python nor its standard library provides a canonical way to compose functions, so we make one:

compose = lambda f, g: lambda *args, \
    **kwargs: f(g(*args, **kwargs))

With that in place, we can easily make form fields that use the HTML5 “required” attribute:

CharField = compose(html5_required, CharField)

And we can use these fields as we do normally:

class SampleForm(form):
    text_field = CharField()