Django Form Snippets
Recently updated on
Django forms are very convenient tools for Django development, but sometimes I find myself wrangling with them to get them to do what I want them to do. I’ve compiled a few simple form examples that I find useful for various form-related tasks.
Say you have a model that looks like this:
Example 1:
class MyModel(models.Model): myfield1= models.CharField(u'Field 1 Name', help_text='Field 1 help', max_length=20, blank=True) myfield2 = models.CharField(u'Field 2 Name', help_text='Field 2 help', max_length=20, blank=True)
As we know, generating a basic ModelForm for this model is as simple as setting the model in the ModelForm’s Meta subclass. However, you may want to override the default widget or required attribute (or something else) generated by the ModelForm.
A lot of simple Django examples recommend that you override these defaults by setting attributes in the ModelForm with the same names as those in the model. I personally don’t like that approach, as this type of override clobbers any ModelForm attributes that normally would have been carried over from the field in the model. For example, if you had set a verbose_name, blank/null parameters, or the help_text setting on the model field, this override will clear those attributes, forcing you to redefine them in the form. This is not very DRY. (See Example 2)
Example 2:
class MyForm1(forms.ModelForm): # As you can see, we have to define label and help_text again in the form when # all we really want to change, in this case, is the widget myfield1 = forms.CharField( label=”My Field 1 Name”, help_text=”My Field 1 help”, widget=forms.Textarea(attrs=‘class’:’myclass’,})) class Meta: model = models.MyModel
To solve this duplication of work, one standard approach is to use the form’s __init__() function to set only the specific field attributes that you want to change. For example, here I can change the default widget and the required attribute without overriding the verbose_name or help_text from the model.
Example 3:
class MyForm2(forms.ModelForm): def __init__(self, *args, **kwargs): super(MyForm2, self).__init__(*args, **kwargs) self.fields['myfield1'].required = True self.fields[‘myfield1’].widget = forms.Textarea(attrs={‘class’:’myclass’,}) class Meta: model = models.MyModel
This works great for redefining specific ModelForm field attributes without trumping the other field attributes taken from the Model, but what if we want to preserve these Model attributes after changing the FormField type?
For example, normally when you put a CharField in a model, the ModelForm will render a CharField form field, but let’s say we want to the ModelField to render an EmailField instead with a Textarea widget. Completely overriding the field, like in Example 2, is one way to achieve this; but again, this will override all of the default attributes on the ModelField, leaving you to redefine them in your form.
Instead, we can access the form field’s associated model field and redefine the default form field that will be rendered by the model field.
Example 4:
class MyForm3(forms.ModelForm): def __init__(self, *args, **kwargs): super(MyForm3, self).__init__(*args, **kwargs) field = self.Meta.model._meta.get_field(‘myfield1’) self.fields[‘myfield1’] = field.formfield(form_class=forms.EmailField) self.fields[’myfield1’].widget = forms.Textarea(attrs={‘class’:’myclass’,}) class Meta: model = models.MyModel
Custom form error messages are a nice option to have when users submit a form. Rather than having the default error message of “This field is required”, for example, you could customize the error message to be more specific. One simple way to override the error messages generated by the form field clean (error messages such as “required” and “invalid” messages defined as error_messages keys), is to change their values stored in the error_messages dictionary associated with each field. This can be done in a form’s __init__() method such as in the example below.
Example 5:
class MyForm4(forms.ModelForm): def __init__(self, *args, **kwargs): super(MyForm4, self).__init__(*args, **kwargs) self.fields['myfield1'].required = True # added to force this example to be required self.fields['myfield1'].error_messages['required'] = ('Myfield is required.') class Meta: model = models.MyModel
Each form field type has its own error_messages keys that can be overridden or appended to.
One thing that drives me up a wall is Django’s default behavior of displaying error messages above an HTML form input rather than below it. Luckily, a simple solution is to override a form’s as_table method and customize the html for the field that will be rendered.
Example 6: Thanks to djangosnippets.org
class MyForm5(forms.ModelForm): def as_table(self): return self._html_output(u'<tr valign="top"><th>' + '%(label)s</th><td>%(field)s%(help_text)s' + '<td>%(errors)s</td></td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', True)
class Meta: model = models.MyModel
Another way to customize your form’s display is to loop through your form fields in a template. This gives you a bit more flexibility then simply “auto-rendering” your form using a {{ form.as_table }} template variable, but also doesn’t require you to create custom html code for every single form input. Notice in the below example that you can access specific field attributes in the template, such as the ‘required’ form field attribute. This is useful because the form can automatically mark fields as required or not when the form is rendered. (In fact, form.field.field is an instance of forms.BoundField and so you can access any BoundField attribute thought field.field.)
Example 7:
<form action="" method="post"> {{ form.non_field_errors }} <table> {% for field in form %} <tr> <th>{{ field.label_tag }} {% if field.field.required %}<span class="requ">*</span>{% endif %}</th> <td>{{ field.errors }}{{ field }} {% if field.help_text %} <p class="help_text">{{ field.help_text }}</p> {% endif %} </td> </tr> {% endfor %} <tr><th></th><td><input type="submit" value="Submit"></td></tr> </table> </form>
Another neat thing that you can do with syntax similar to Example 8 is that you can define custom attributes on your form fields in forms.py and reference them in the template.
Sometimes when “auto-rendering” a Django form, either via something like Example 8 or via the form.as_table method, it’s useful to specify the order in which the form fields are displayed. This can be accomplished by setting the fields.keyOrder form attribute in the form __init__() function.
Example 8:
class MyForm6(forms.ModelForm): def __init__(self, *args, **kwargs): super(MyForm6, self).__init__(*args, **kwargs) # When the form auto-renders, field2 will appear first and field1 will appear second self.fields.keyOrder = ['field2','field1',]
The unfortunate thing about using this keyOrder attribute is that you have to explicitly list every field that you want to display in the list that you assign to it. Otherwise, any fields not listed in the keyOrder list will not display when the form renders. This is a hassle because if you have a form with a lot of fields, you have to list every single field in the keyOrder attribute. And, if you add a field to your form, you have to remember to also add it to the keyOrder list. Here is an approach to prevent this duplicate definition:
Example 9:
class MyForm7(forms.ModelForm):
# define some more form fields here just for the example myfield3 = forms.CharField() myfield4 = forms.CharField()
def __init__(self, *args, **kwargs): super(MyForm7, self).__init__(*args, **kwargs) # use this list to specify which fields should be ordered and # their order ordered_fields = ['myfield2','myfield1',] # this statement lists your ordered fields first (in order) and # then lists all other fields self.fields.keyOrder = ordered_fields + \ [ i for i in self.fields if i not in ordered_fields ]
class Meta: model = models.MyModel
Sometimes it’s nice to be able to test a form interactively from the Django shell. When doing this, you might like to simulate data being POSTed to it. This can be done easily by creating a dictionary with key/value pairs that correspond to post parameters. You can then interact with the form as follows
Example 10:
>>> from a.forms import MyForm1 >>> post_or_get_data = {u'myfield1':[u'a'], u'myfield2':[u'b']} >>> f = MyForm1(data=post_or_get_data) >>> f <a.forms.MyForm1 object at 0x969284c> >>> f.is_bound True >>> f.errors {} >>> f.data {u'myfield2': [u'b'], u'myfield1': [u'a']} >>> f.cleaned_data {'myfield2': u"[u'b']", 'myfield1': u"[u'a']"}
I hope you find these examples useful. If you know of an interesting way to utilize Django forms, or if you see a better approach for one of the examples above, please share in the comments below!
For working examples of the code discussed above, download the Django example project.