Posts tagged forms
jQuery Validation with Django Forms
Mar 8th
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.
Validation Rules
jQuery validation works by assigning validation rules to each element in your form. These rules can be assigned a couple different ways:
- Class Rules
- Metadata Rules
- Rules Object
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.
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}'
}))
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 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 validation 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:
- make jQuery Validation output an error list
- 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 options to validate 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 %}Django Model Formsets
Mar 1st
Django model formsets provide a way to edit multiple model instances within a single form. This is especially useful for editing related models inline. Below is some knowledge I’ve collected on some of the lesser documented and undocumented features of Django’s model formsets.
Formset Factory Methods
Model Formsets are generally created using a factory method. The default is modelformset_factory, which wraps formset_factory to create Model Forms. You can also create inline formsets to edit related objects, using inlineformset_factory. inlineformset_factory wraps modelformset_factory to restrict the queryset and set the initial data to the instance’s related objects.
Adding Fields to a Formset
Just like with a normal formset, you can add additional fields to a model formset by creating a base formset class with an add_fields method, then passing it in to the factory method. The only difference is the class you inherit from. For inlineformset_factory, you should inherit from BaseInlineFormSet.
If you’re using modelformset_factory, then you should import and inherit from BaseModelFormSet instead. Also remember that form.instance may be used to set initial data for the fields you’re adding. Just check to make sure form.instance is not None before you try to access any properties.
from django.forms.models import BaseInlineFormSet, inlineformset_factory
class BaseFormSet(BaseInlineFormSet):
def add_fields(self, form, index):
super(BasePlanItemFormSet, self).add_fields(form, index)
# add fields to the form
FormSet = inlineformset_factory(MyModel, MyRelatedModel, formset=BaseFormSet)
Changing the Default Field
If you’d like to customize one or more of the form fields within your model formset, you can create a formfield_callback function and pass it to the formset factory. For example, if you want to set required=False on all fields, you can do the following.
def custom_field_callback(field):
return field.formfield(required=False)
FormSet = modelformset_factory(model, formfield_callback=custom_field_callback)
field.formfield() will create the default form field with whatever arguments you pass in. You can also create different fields, and use field.name to do field specific customization. Here’s a more advanced example.
def custom_field_callback(field):
if field.name == 'optional':
return field.formfield(required=False)
elif field.name == 'text':
return field.formfield(widget=Textarea)
elif field.name == 'integer':
return IntegerField()
else:
return field.formfield()
Deleting Models in a Formset
Pass can_delete=True to your factory method, and you’ll be able to delete the models in your formsets. Note that inlineformset_factory defaults to can_delete=True, while modelformset_factory defaults to can_delete=False.
Creating New Models with Extra Forms
As with normal formsets, you can pass an extra argument to your formset factory to create extra empty forms. These empty forms can then be used to create new models. Note that when you have extra empty forms in the formset, you’ll get an equal number of None results when you call formset.save(), so you may need to filter those out if you’re doing any post-processing on the saved objects.
If you want to set an upper limit on the number of extra forms, you can use the max_num argument to restrict the maximum number of forms. For example, if you want up to 6 forms in the formset, do the following:
MyFormSet = inlineformset_factory(MyModel, MyRelatedModel, extra=6, max_num=6)
Saving Model Formsets
Model formsets have a save method, just like with model forms, but in this case, you’ll get a list of all modified instances instead of a single instance. Unmodified instances will not be returned. As mentioned above, if you have any extra empty forms, then those list elements will be None.
If you want to create custom save behavior, you can override 2 methods in your BaseFormSet class: save_new and save_existing. These methods look like this:
from django.forms.models import BaseInlineFormSet
class BaseFormSet(BaseInlineFormSet):
def save_new(self, form, commit=True):
# custom save behavior for new objects, form is a ModelForm
return super(BaseFormSet, self).save_new(form, commit=commit)
def save_existing(self, form, instance, commit=True):
# custom save behavior for existing objects
# instance is the existing object, and form has the updated data
return super(BaseFormSet, self).save_existing(form, instance, commit=commit)
Inline Model Admin
Django’s Admin Site includes the ability to specify InlineModelAdmin objects. Subclasses of InlineModelAdmin can use all the arguments of inlineformset_factory, plus some admin specific arguments. Everything mentioned above applies equally to InlineModelAdmin arguments: you can specify the number of extra forms, the maximum number of inline forms, and even your own formset with custom save behavior.
Django Tools and Links
Nov 9th
Using Django
- Top 10 tips to a new django developer : Dpeepul Blog
- Henrique C. Alves – Keeping simple with Django
- Django Dose – Handling Development, Staging, and Production Environments
- Django signals | Mercurytide
Social Apps
- uswaretech’s Django-Socialauth at master – GitHub
- Django-SocialAuth – Login via twitter, facebook, openid, yahoo, google using a single app. — The Uswaretech Blog – Django Web Development
Forms
- django-simple-captcha – Project Hosting on Google Code
- Marco Fucci – Integrating reCAPTCHA with Django
- simonw’s django-safeform at master – GitHub
Notifications
Geolocation
Misc
- taras’s Django-Scraper at master – GitHub
- Semantic Django – Tools for semantic stuff in Django
- sorl-thumbnail – Project Hosting on Google Code
- The apps that power Django-Mingus | Monty Lounge Blog
- fullhistory – Project Hosting on Google Code
- jaycee’s weaver at master – GitHub
- zain’s jogging at master – GitHub
Useful jQuery Plugins
Nov 5th
Django Datetime Snippets
Apr 13th
I’ve started posting over at Django snippets, which is a great resource for finding useful bits of functionality. My first set of snippets is focused on datetime conversions.
The Snippets
FuzzyDateTimeField is a drop in replacement for the standard DateTimeField that uses dateutil.parser with fuzzy=True to clean the value, allowing the parser to be more liberal with the input formats it accepts.
The isoutc template filter produces an ISO format UTC datetime string from a timezone aware datetime object.
The timeto template filter is a more compact version of django’s timeuntil filter that only shows hours & minutes, such as “1hr 30min”.
JSON encode ISO UTC datetime is a way to encode datetime objects as ISO strings just like the isoutc template filter.
JSON decode datetime is a simplejson object hook for converting the datetime attribute of a JSON object to a python datetime object. This is especially useful if you have a list of objects that all have datetime attributes that need to be decoded.
Use Case
Imagine you’re making a time based search engine for movies and/or events. Because your data will span many timezones, you decide that all dates & times should be stored on the server as UTC. This pushes local timezone conversion to the client side, where it belongs, simplifying the server side data structures and search operations. You want your search engine to be AJAX enabled, but you don’t like XML because it’s so verbose, so you go with JSON for serialization. You also want users to be able to input their own range based queries without being forced to use specific datetime formats. Leaving out all the hard stuff, the above snippets can be used for communication between a django webapp and a time based search engine.

























