StreamHacker Weotta be Hacking

8Mar/1016

jQuery Validation with Django Forms

Django has everything you need to do server-side validation, but it's also a good idea to do client-side validation. Here's how you can integrate the jQuery Validation plugin with your Django Forms.

jQuery Validation Rules

jQuery validation works by assigning validation rules to each element in your form. These rules can be assigned a couple different ways:

  1. Class Rules
  2. Metadata Rules
  3. Rules Object

Django Form Class Rules

The simplest validation rules, such as required, can be assigned as classes on your form elements. To do this in Django, you can specify custom widget attributes.

from django import forms
from django.forms import widgets

class MyForm(forms.Form):
    title = forms.CharField(required=True, widget=widgets.TextInput(attrs={
        'class': 'required'
    }))

In Django 1.2, there's support for a required css class, but you can still use the technique above to specify other validation rules.

Django Form Metadata Rules

For validation methods that require arguments, such minlength and maxlength, you can create metadata in the class attribute. You'll have to include the jQuery metadata plugin for this style of rules.

from django import forms
from django.forms import widgets

class MyForm(forms.Form):
    title = forms.CharField(required=True, minlength=2, maxlength=100, widget=widgets.TextInput(attrs={
        'class': '{required:true, minlength:2, maxlength:100}'
    }))

jQuery Validate Rules Object

If your validation requirements are more complex, or you don't want to use the metadata plugin or class based rules, you can create a rules object to pass as an option to the validate method. This object can be generated in your template like so:

<script type="text/javascript">
FORM_RULES = {
    '{{ form.title.name }}': 'required'
};

$(document).ready(function() {
    $('form').validate({
        rules: FORM_RULES
    });
});
</script>

The reason I suggest generating the rules object in your template is to avoid hardcoding the field name in your javascript. A rules object can also be used in conjunction with class and metadata rules, so you could have some rules assigned in individual element classes or metadata, and other rules in your rules object.

Error Messages

If you want to keep the client-side validation error messages consistent with Django's validation error messages, you'll need to copy Django's error messages and specify them in the metadata or in a messages object.

Metadata Messages

Messages must be specified per-field, and per-rule. Here's an example where I specify the minlength message for the title field.

from django import forms
from django.forms import widgets

class MyForm(forms.Form):
    title = forms.CharField(minlength=2, widget=widgets.TextInput(attrs={
        'class': '{minlength:2, messages:{minlength:"Ensure this value has at least 2 characters"}}'
    }))

Messages Object

Messages can also be specified in javascript object, like so:

<script type="text/javascript">
FORM_RULES = {
    '{{ form.title.name }}': 'required'
};

FORM_MESSAGES = {
    '{{ form.title.name }}': 'This field is required'
};

$(document).ready(function() {
    $('form').validate({
        rules: FORM_RULES,
        messages: FORM_MESSAGES
    });
});
</script>

Just like with validation rules, messages in element metadata can be used in conjunction with a global messages object. Note: if an element has a title attribute, then the title will be used as the default error message, unless you specify ignoreTitle: false in the jQuery validate options.

Error Labels vs Errorlist

Django's default error output is an error list, while the default for jQuery Validation errors is a label with class="error". So in order to unify your validation errors, there's 2 options:

  1. make jQuery Validation output an error list
  2. output error labels instead of an error list in the template

Personally, I prefer the simple error labels produced by jQuery validation. To make Django generate those instead of an error list, you can do the following in your templates:

{{ field }}
{% if field.errors %}
{# NOTE: must use id_NAME for jquery.validation to overwrite error label #}
<label class='error' for='id_{{ field.name }}' generated="true">{{ field.errors|join:". " }}</label>
{% endif %}

You could also create your own error_class for outputting the error labels, but then you'd lose the ability to specify the for attribute.

If you want to try to make jQuery validation produce an error list, that's a bit harder. You can specify a combination of jQuery validation options and get a list, but there's not an obvious way to get the errorlist class on the ul.

$('form').validate({
    errorElement: 'li',
    wrapper: 'ul'
});

Other options you can look into are errorLabelContainer, errorContainer, and a highlight function.

Final Recommendations

I find it's easiest to specify class and metadata rules in custom widget attributes 90% of the time, and use a rules object only when absolutely necessary. For example, if I want to require only the first elements in a formset, but not the rest, then I may use a rules object in addition to class and metadata rules. For error messages, I generally use a field template like the above example that I include for each field:

{% with form.title as field %}{% include "field.html" %}{% endwith %}

Or if the form is really simple, I do

{% for field in form %}{% include "field.html" %}{% endfor %}
  • gerardp

    Do you know dajax?
    “easy to use AJAX libraries for django”

    http://dajaxproject.com/

  • gerardp

    Do you know dajax?
    “easy to use AJAX libraries for django”

    http://dajaxproject.com/

  • http://streamhacker.com/ Jacob Perkins

    Thanks, I didn’t know about dajax.

  • Jacob

    Thanks, I didn’t know about dajax.

  • http://d-w.me/ Dan Ward

    Nice guide, although may I add…

    To prevent repeating yourself with the initial widget class attributes, it may be worth creating a wrapper, which iterates over each field, checking the required, max_length and min_length values, then applying the class respectively.

    This way, you prevent the potential issue of having validation errors for data, which may, in fact be valid.

    Of course, the other option is to output the field however you like and handle the form.fieldname.field.required (etc) attributes in the template and applying the class there.

    Only a suggestion and I’m sure many will find the article very helpful :)

  • http://d-w.me Dan Ward

    Nice guide, although may I add…

    To prevent repeating yourself with the initial widget class attributes, it may be worth creating a wrapper, which iterates over each field, checking the required, max_length and min_length values, then applying the class respectively.

    This way, you prevent the potential issue of having validation errors for data, which may, in fact be valid.

    Of course, the other option is to output the field however you like and handle the form.fieldname.field.required (etc) attributes in the template and applying the class there.

    Only a suggestion and I’m sure many will find the article very helpful :)

  • http://d-w.me/ Dan Ward

    Just to add, the wrapper method would run inside the form’s ‘__init__’ method, AFTER super’ing it.


    Dan Ward:

    To prevent repeating yourself with the initial widget class attributes, it may be worth creating a wrapper, which iterates over each field, checking the required, max_length and min_length values, then applying the class respectively.

  • http://d-w.me Dan Ward

    Just to add, the wrapper method would run inside the form’s ‘__init__’ method, AFTER super’ing it.


    Dan Ward:

    To prevent repeating yourself with the initial widget class attributes, it may be worth creating a wrapper, which iterates over each field, checking the required, max_length and min_length values, then applying the class respectively.

  • http://streamhacker.com/ Jacob Perkins

    Great idea, Dan. Unfortunately, I’m just not sure how reusable that wrapper would be. I find myself creating fields in so many different ways (templates, formsets with form_field_callback, etc) that I’d be lucky to use it 50% of the time. But the idea did come to mind while writing this, and your clarification makes me think that a generic ValidationForm superclass could handle the __init__ field class assignment.

  • Jacob

    Great idea, Dan. Unfortunately, I’m just not sure how reusable that wrapper would be. I find myself creating fields in so many different ways (templates, formsets with form_field_callback, etc) that I’d be lucky to use it 50% of the time. But the idea did come to mind while writing this, and your clarification makes me think that a generic ValidationForm superclass could handle the __init__ field class assignment.

  • as

    dd

  • as

    ddd

  • as

    dd

  • as

    ddd

  • as

    aaa

  • as

    ddd

%d bloggers like this: