Set up the layout for Django forms in templates

Problem

You want to set up the layout for Django forms in templates.

Solution

You can quickly output all the fields in Django form instance using the form.as_table, form.as_p or form.as_ul methods. If you want to granularly output form fields in a template, you'll have to use one of multiple attributes available through the form.<field_name> syntax (e.g. {{form.<field_name>}} to output the HTML form tag, {{form.<field_name>.errors}} to output errors associated with a field).

By default, Django outputs a form's fields in the same order as they're defined in the form class. To override this behavior you can use the field_order or order_fields option to define a new output field order.

You can associate a CSS class to all Django form fields that contain an error with the error_css_class option, similarly you can associate a CSS class to all Django forms fields that are required with the required_css_class option. To get full control of a form field's CSS class assignment and other HTML attribute assignments, you must assign a custom Django widget to the form field in question.

You can output a form's errors through the errors dictionary, where each dictionary key represents the form field name and the value is a list of error messages. In addition, you can output form errors not associated with a specific field through non_field_errors.

How it works

When you pass a Django form instance -- unbound or bound -- to a template there are many options to choose from to generate its layout. You can use one of Django's pre-built HTML helpers to quickly generate a layout or you can opt to granularly output each field to create a more sophisticated layout with responsive design. In addition, you may want to display form errors in many different ways, such as besides the fields themselves or at the top of a form. Up next, I'll describe the various options to output Django forms in templates. Listing 1 shows the Django form I'll use throughout the remaining sections.

Listing 1 - Django form class definition


from django import forms

class ContactForm(forms.Form):
      name = forms.CharField(required=False)
      email = forms.EmailField(label='Your email')  
      comment = forms.CharField(widget=forms.Textarea)

Output form fields: form.as_table, form.as_p, form.as_ul & granularly by field

Django forms offer three helper methods to simplify the output of all form fields. The syntax form.as_table outputs a form's fields to accommodate an HTML <table> as illustrated in listing 2. The syntax form.as_p outputs a form's fields with HTML <p> tags as illustrated in listing 3. Where as the syntax form.as_ul outputs a form's fields to accommodate an HTML <ul> list tag.

Note If you use form.as_table, form.as_p, form.as_ul you must declare opening/closing HTML tags, a wrapping <form> tag, a Django {% csrf_token %} tag and an <input type="submit"> button.

Although the form.as_table, form.as_p and form.as_ul helper methods save you work, be aware they do not output a functional web form. You must still add an opening/closing <table> & <ul> tag for form.as_table and form.as_ul, a wrapping <form> tag in all cases, as well as a Django {% csrf_token %} tag and an <input type="submit"> button in all cases.

This is described in detail in the Set up Django forms and understand their structure and workflow recipe, specifically in the Functional web form syntax for Django forms.

Listing 2 - Django form output with form.as_table


<tr>
    <th><label for="id_name">Name:</label></th>
    <td><input id="id_name" name="name" type="text" /></td>
</tr>\n
<tr>
    <th><label for="id_email">Your email:</label></th>
    <td><input id="id_email" name="email" type="email" required/></td>
</tr>\n
<tr>
    <th><label for="id_comment">Comment:</label></th>
    <td><textarea cols="40" id="id_comment" name="comment" rows="10" required>\r\n</textarea></td>
</tr>

Listing 3 - Django form output with form.as_p


<p>
    <label for="id_name">Name:</label> <input id="id_name" name="name" type="text" />
</p>\n
<p>
    <label for="id_email">Your email:</label> <input id="id_email" name="email" type="email" required/>
</p>\n
<p>
    <label for="id_comment">Comment:</label> <textarea cols="40" id="id_comment" name="comment" rows="10" required>\r\n</textarea>
</p>'


Listing 4 - Django form output with form.as_ul


<li>
    <label for="id_name">Name:</label> <input id="id_name" name="name" type="text" />
</li>\n
<li>
    <label for="id_email">Your email:</label> <input id="id_email" name="email" type="email" required/>
</li>\n
    <li><label for="id_comment">Comment:</label> <textarea cols="40" id="id_comment" name="comment" rows="10" required>\r\n</textarea>
</li>

Note You can also use label_suffix, auto_id=False and field_order to alter form.as_table, form.as_p & as_ul output

The form.as_table, form.as_p & as_ul output can be made less verbose -- omitting label tags and id attributes -- by initializing the form with auto_id=False. In addition, you can also change the symbol that separates label names (by default :) with another symbol by initializing the form with the label_suffix variable. It's also possible to use the field_order option to alter the output field order. See the form processing recipe and the remainder of this recipe for more details.

Under certain circumstances none of the previous helper methods may be sufficient to achieve certain form layouts. For example, if you're creating a responsive design you'll need to output each of the fields manually to accommodate the very specific layout required to satisfy this requirement (e.g. Bootstrap CSS grid columns). To achieve the custom output of fields every form instance permits access to its fields through the form.<field_name> syntax using the attributes in table 1.

Table 1 - Django form field attributes accessible in templates
Attribute nameDescription
{{form.<field_name>}} Outputs the HTML form tag -- technically known as the Django widget -- associated with the field (e.g. <input type="text">)
{{form.<field_name>.name}} Outputs the name of a field, as defined in the form class.
{{form.<field_name>.value}} Outputs the value of the field assigned with initial or user provided data. Useful if you need to separatly output the HTML form tag's value attribute (e.g. <input type="text" value="John Doe">, if you just want to output John Doe)
{{form.<field_name>.label}} Outputs the label of a field, which by default uses the syntax Your <field_name> (e.g. for the field email, form.email.label outputs Your email.
{{form.<field_name>.id_for_label}} Outputs the label id of a field, which by default uses the syntax id_<field_name> (e.g. for the field email, form.email.id_for_label outputs id_email).
{{form.<field_name>.auto_id}} Outputs the auto id of a field, which by default uses the syntax id_<field_name> (e.g. for the field email, form.email.auto_id outputs id_email.
{{form.<field_name>.label_tag}} Helper method to output the HTML <label> tag along with id_for_label and label(e.g. for the field email, form.email.label_tag outputs <label for="id_email">Your email:</label>.
{{form.<field_name>.help_text}} Outputs the help text associated with a field.
{{form.<field_name>.errors}} Outputs the errors associated with a field.
{{form.<field_name>.css_classes}} Outputs the CSS classes associated with a field.
{{form.<field_name>.as_hidden}} Outputs the HTML of a field as a hidden HTML field (e.g. <input type="hidden" >)
{{form.<field_name>.is_hidden}} Boolean result of a field's hidden status.
{{form.<field_name>.as_text}} Outputs the HTML of a field as a text HTML field (e.g. <input type="text" >)
{{form.<field_name>.as_textarea}} Outputs the HTML of a field as a textarea HTML field (e.g. <textarea></textarea>)
{{form.<field_name>.as_widget}} Outputs the Django widget associated with a field; technically produces the same output as calling the standalone field with the syntax {{form.<field_name>}}.
Note Use label, label_suffix, help_text on form fields to override their default value

You can override the default output for {{form.<field_name>.label}}, the suffix for {{form.<field_name>.label_tag}} and the default output for {{form.<field_name>.help_text}} in table 1, by using the label, label_suffix and help_text options on form fields. This process is described in the previous recipe Django form field types: Widgets, options and validations

As you can see in table 1, there are many field attributes available to customize the layout of a form. Just be careful that if you output form fields granularly you don't miss a field, because if you do miss a field the most likely outcome is Django won't be able to process the form as it won't receive a value for the missing field. Listing 5 illustrates a standard {% for %} loop which ensures you don't miss any field and provides more flexibility than the previous form.as_table, form.as_p & as_ul methods.

Listing 5 - Django form {% for %} loop over all fields

{% for field in form %}
     <div class="row">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <div class="row">
              {{ field.help_text }}
        </div>
        {% endif %}
    </div>
{% endfor %}

As you can see in listing 5, by creating a loop over the form reference you ensure no fields are missed. If you want to avoid presenting a field in certain form layouts, then I recommend you use the {{field.as_hidden}} vs. {{field}}, as this ensures the field still forms part of the form for validation purposes and is simply hidden from a user.

Output field order: field_order and order_fields.

If you use any of the techniques presented in listings 2, 3, 4 or 5, the form fields are output in the same order as they're declared in the form class in listing 1 (i.e. name,email,comment). However, you can use several techniques to alter the order in which form fields are output.

The first and obvious approach is to change the form field order directly in the form class definition. Because this last technique requires altering a form's source code considerably, Django also offers the field_order option. The field_order option accepts a list of form field names in the order you want them output (e.g. field_order=['email','name','comment'] outputs the email field first, followed by name and comment). The field_order option is flexible enough that you can provide a partial list of form fields (e.g. field_order=['email'] outputs the email field first and the remaining form fields in their declared order) as well as declare non-existent field names which are ignored and is helpful when using form inheritance.

The field_order option can be declared in two locations. First, it can be declared as part of a form class definition, as illustrated in listing 6.

Listing 6 - Django form field_order option to enforce field order


from django import forms

class ContactForm(forms.Form):
      name = forms.CharField(required=False)
      email = forms.EmailField(label='Your email')
      comment = forms.CharField(widget=forms.Textarea)
      field_order = ['email','comment','name']
      

As you can see in listing 6, field_order is declared as any other form field and assigned a list of field names to ensure the fields are output in the order: email, comment and name. It's also possible to use the field_order option as part of a form's initialization processes -- described in detail in the form processing recipe. It's worth mentioning that if you use the field_order option on both the class definition -- as shown in listing 6 -- and form instance initialization, the latter value takes precedence over the former.

In addition to the field_order option, Django also offers order_fields which also expects a list of field names to alter a form's output field order. But unlike the field_order option which must be declared in a form class or as part of the initialization of a form instance, order_fields can be called directly on a form instance which makes it a good option to use in a view method or template (e.g. form.order_fields(['email'])).

Output CSS classes, styles & field attributes: error_css_class, required_css_class, widget customization and various form field options.

By default, when you output form fields and labels there are no CSS classes or styles associated with them. Django offers several mechanisms to associate CSS classes with form fields. The first two approaches are the error_css_class and required_css_class fields which are declared directly in a Django form, as illustrated in listing 7.

Listing 7 - Django form error_css_class and required_css_class fields to apply CSS formatting


from django import forms

class ContactForm(forms.Form):
      name = forms.CharField(required=False)
      email = forms.EmailField(label='Your email')
      comment = forms.CharField(widget=forms.Textarea)
      error_css_class = 'error'
      required_css_class = 'bold'
      

As you can see in listing 7, the error_css_class and required_css_class fields are added just like regular form fields. When a field associated with a form instance of this kind is rendered on a template, Django adds the error CSS class to all fields marked with an error and adds the bold CSS class to all fields marked as required.

For example, all form fields are treated as required except when they explicitly use required=False. This means if you output an unbound form instance from listing 7 using form.as_p, the comment field is output as <p class="bold">Comment: <textarea cols="40" name="comment" rows="10" required>\r\n</textarea></p> -- note the class in the <p> tag. Similarly, if a field associated with a bound form instance from listing 7 raises an error, Django adds the error CSS class to the field (e.g. if the email field value is not valid, the email field is output as <p class="bold error">Your email: <input name="email" type="email" value="justastring" required /></p>, note the bold CSS class remains because the form field is also required per the definition).

As helpful as the error_css_class and required_css_class fields are, they still offer limited CSS formatting functionality. To gain full control over CSS class output, you'll need to either use some of the more granular output options for fields in table 1 or customize a form field's widget as illustrated in listing 8.

Listing 8 - Django form with inline widget definition to add custom CSS class


from django import forms

class ContactForm(forms.Form):
      name = forms.CharField(required=False)
      email = forms.EmailField(label='Your email', widget=forms.TextInput(attrs={'class' : 'myemailclass'}))
      comment = forms.CharField(widget=forms.Textarea)

Notice in listing 8 how the email field is declared with the widget=forms.TextInput(attrs={'class' : 'myemailclass'}) argument. This last statement tells Django that when it outputs the email field, it use the custom forms.TextInput widget which declares the CSS class attribute with the myemailclass value. By using the form definition in listing 8, the email field is output as <input class="myemailclass" type="text"...>. If you don't know what a Django widget is or how it relates to a Django form field, I advise you to look over the section What are Django widgets ? And what is their relationship with Django form fields ? in the previous recipe.

The approach presented in listing 8 is a powerful technique, because just as you can declare the CSS class attribute, you can also declare any other form field HTML attribute. For example, if you wanted to declare custom HTML attributes -- such as those used by frameworks like jQuery or Bootstrap -- you can easily use this same technique (e.g.widget=forms.TextInput(attrs={'role' : 'dialog'}) would output <input role="dialog" type="text"...>).

However, a word of caution now that you know how easy it's to output any HTML attribute alongside a Django form field. Be aware that nearly all Django form field data types come with built-in options that get translated into HTML attributes. For example, the forms.CharField(max_length=25) statement gets output to <input type="text" maxlength="25"...>, which means the form field max_length option automatically generates the HTML maxlength="25" attribute. So be careful to start adding HTML attributes indiscriminately using the approach in listing 8, as they may already be supported through built-in data type options. See the previous recipe Django form field types: Widgets, options and validations for more details on these built-in data type options.

Output form field errors: form.<field_name>.errors, form.errors, form.non_field_errors

Just as form fields can be output in different ways, form field errors can also be output in different ways. Toward the end of the first section in listing 5, you can see how we use the {{field.errors}} syntax to output errors associated with a particular field. If you use the form.<field_name>.errors syntax keep in mind its output is an HTML formatted list, as illustrated in listing 9.

Listing 9 - Django form form.<field_name>.errors HTML output

<ul class="errorlist">
    <li>Name is required.</li>
</ul>

As you can see in listing 9, the error list contains the errorlist CSS class -- which allows you to provide CSS behaviors like a background color or borders -- and can contain a list of errors associated with a field, albeit listing 9 shows a single error. If you want to strip these wrapping HTML list tags to gain more control over the layout (e.g. creating a responsive design or CSV list) you can do so creating a loop as illustrated in listing 10.

Listing 10 - Django form form.<field_name>.errors with no HTML output due to loop

{% for error in form.<field_name>.errors %}
   <div class="row">{{error}}</div><!-- Based on listing 9, {{error}} outputs 'Name is required.' -->
{% endfor %}

Besides the form.<field_name>.errors syntax to access field errors, Django also provides a form instance errors dictionary and non_field_errors. The form.errors dictionary is really just an aggregated version of all the form.<field_name>.errors in a form, where each dictionary key represents the form field name and the value is a list of error messages with the additional code property (e.g. required) to further filter the error list. If you want to output every form error at the top of a form/page or require error filtering by code type, then accessing form.errors is the way to go.

In addition to specific form field errors, Django forms often have what are called non-field errors, which are errors that aren't specifically associated with a field. Because non-field errors don't apply to a specific form field, it's common to output these type of errors at the top of a form accessing non_field_errors on a form instance. Note the use and creation of non-field errors is discussed in the previous recipe in Error form values: errors.

Be aware that if you use form.errors or form.non_field_errors to output errors, by default each list of errors is wrapped in an HTML formatted list -- like listing 9 -- but you can add an additional for loop to the error list -- as in listing 10 -- to create a custom HTML layout.

Finally, it's worth mentioning there are a series of auxiliary methods designed to facilitate error output (e.g. in JSON format), Table 1 in the Django form processing recipe describes these methods.