Rendering Forms¶
New in version 0.6.
Djem provides some simple template tags to assist in rendering form fields in templates.
While Django provides numerous ways to customise the way it renders form fields, they are often inadequate. The options for rendering all of a form’s fields as a series of <tr>
, <li>
, or <p>
tags, are limited to providing fairly basic field/label pairs, with little opportunity for customisation. And rendering fields manually often results in repeating boilerplate code, e.g. specifying a wrapping element, the field, its label, its error list, etc.
The form_field
and checkbox
template tags use a fully customisable template to render each individual field. The template, found at djem/form_field.html
, can be altered per-project using Django’s standard template overriding mechanism. By default, it provides:
A wrapping element with the following features:
By default, the element is a
<div>
, but this can be modified using theDJEM_FORM_FIELD_TAG
setting.The element is given the following CSS classes:
form-field
Any classes defined by the form. See below examples.
Any classes passed to the template tag. See below examples.
The element is given additional HTML attributes based on keyword arguments passed to the template tag. See below examples.
The field’s label (for fields rendered with
form_field
, see below examples).The field’s error list (when applicable). The error list is the Django default
<ul class="errorlist">
.The field itself.
The specified checkbox label (for fields rendered with
checkbox
, see below examples). This<label>
element will have thecheck-label
CSS class.A
<div>
with the CSS classform-field__help
containing the field’s help text, if any.
The following usage examples are based off a basic user registration form:
from django import forms
class RegistrationForm(forms.Form):
first_name = forms.CharField(label='Your first name')
last_name = forms.CharField(label='Your last name')
email = forms.EmailField(label='Your email address', help_text="Don't worry, we won't share your email address with anyone.")
terms = forms.BooleanField(label='Terms of Service')
In the below template snippets, an instance of this form is contained in a context variable called registration_form
.
form_field
¶
The form_field
tag has a single required argument: the form field to be rendered.
{% load djem %}
...
{% form_field registration_form.first_name %}
{% form_field registration_form.last_name %}
{% form_field registration_form.email %}
{% form_field registration_form.terms %}
...
Rendering this template would result in the following HTML:
<div class="form-field">
<label for="id_first_name">Your first name:</label>
<input type="text" name="first_name" id="id_first_name" />
</div>
<div class="form-field">
<label for="id_last_name">Your last name:</label>
<input type="text" name="last_name" id="id_last_name" />
</div>
<div class="form-field">
<label for="id_email">Your email address:</label>
<input type="email" name="email" id="id_email" />
<div class="form-field__help">Don't worry, we won't share your email address with anyone.</div>
</div>
<div class="form-field">
<label for="id_terms">Terms of Service:</label>
<input type="checkbox" name="terms" id="id_terms" />
</div>
This doesn’t look too different from what {{ registration_form.as_p }}
would generate, but note:
The
<div>
wrapper around each field can be customised without even overriding the template, using theDJEM_FORM_FIELD_TAG
setting.The
form-field
class provides an easy CSS styling target.Help text is rendered automatically when defined, with its own easily targetted
field-field__help
class for styling.The entire block rendered for each field can be completely customised by overriding the
djem/form_field.html
template, allowing for custom and consistent rendering of form fields across your project.
The form_field
tag has more advanced uses, too. But first, the “terms” checkbox rendered above could be nicer, and that’s what the checkbox
tag is for.
checkbox
¶
The checkbox
tag uses the same djem/form_field.html
template as form_field
, but it renders labels differently:
it includes the
<label>
element after the field itself, not beforeit gives the
<label>
element thecheck-label
CSS class, allowing it to be styled independently of regular labels
Also, unlike form_field
, checkbox
is a block tag. It uses the content between its start and end tags as the label for the field. This has one important benefit: we can include HTML in the label text. Updating the previous example to use the checkbox
tag for the “terms” field:
{% load djem %}
...
{% checkbox registration_form.terms %}
I agree to the <a href="{% url 'terms' %}" target="_blank">Terms of Service</a>.
{% endcheckbox %}
...
The rendered HTML for this field then becomes:
<div class="form-field">
<input type="checkbox" name="terms" id="id_terms" />
<label class="check-label" for="id_terms">I agree to the <a href="{% url 'terms' %}" target="_blank">Terms of Service</a>.</label>
</div>
That’s a bit more user-friendly.
If no content is entered between the start and end tags, the field’s default label text is used. In this case, the <label>
element will still be included after the field itself, instead of before, and will still receive the check-label
class.
Note
Don’t go too crazy with HTML in your label text. It is still rendered inside a <label>
element, so should only contain markup that is valid within <label>
.
Note
checkbox
is not strictly limited to actual checkbox inputs. You could, if for some reason it was appropriate, use it for any form field.
Extra CSS classes¶
Both form_field
and checkbox
can add additional field-specific CSS classes to the wrapping element. These classes can come from two places: the Form
class definition, or passing them into the template tag itself.
A Form
class can specify additional CSS classes per field in several ways. The error_css_class
and required_css_class
attributes can be used to automatically apply additional classes to fields that are required or contain errors, respectively. Also, custom classes can be applied to specific fields using their css_classes()
method.
Adapting the registration form example to add a CSS class to highlight required fields is simple:
from django import forms
class RegistrationForm(forms.Form):
required_css_class = 'form-field--required'
first_name = forms.CharField(label='Your first name')
last_name = forms.CharField(label='Your last name')
email = forms.EmailField(label='Your email address', help_text="Don't worry, we won't share your email address with anyone.")
terms = forms.BooleanField(label='Terms of Service')
Without changing the template, this generates the following HTML:
<div class="form-field form-field--required">
<label for="id_first_name">Your first name:</label>
<input type="text" name="first_name" id="id_first_name" />
</div>
<div class="form-field form-field--required">
<label for="id_last_name">Your last name:</label>
<input type="text" name="last_name" id="id_last_name" />
</div>
<div class="form-field form-field--required">
<label for="id_email">Your email address:</label>
<input type="email" name="email" id="id_email" />
<div class="form-field__help">Don't worry, we won't share your email address with anyone.</div>
</div>
<div class="form-field form-field--required">
<label for="id_terms">Terms of Service:</label>
<input type="checkbox" name="terms" id="id_terms" />
</div>
The other way to apply custom CSS classes to a field is to pass them directly into the template tag. For example, in order to display the first_name
and last_name
fields side-by-side, simply pass in a CSS class that can style them accordingly:
{% load djem %}
...
{% form_field registration_form.first_name 'one-half' %}
{% form_field registration_form.last_name 'one-half' %}
{% form_field registration_form.email %}
{% form_field registration_form.terms %}
...
<div class="form-field form-field--required one-half">
<label for="id_first_name">Your first name:</label>
<input type="text" name="first_name" id="id_first_name" />
</div>
<div class="form-field form-field--required one-half">
<label for="id_last_name">Your last name:</label>
<input type="text" name="last_name" id="id_last_name" />
</div>
<div class="form-field form-field--required">
<label for="id_email">Your email address:</label>
<input type="email" name="email" id="id_email" />
<div class="form-field__help">Don't worry, we won't share your email address with anyone.</div>
</div>
<div class="form-field form-field--required">
<label for="id_terms">Terms of Service:</label>
<input type="checkbox" name="terms" id="id_terms" />
</div>
Extra HTML attributes¶
form_field
and checkbox
also support adding custom HTML attributes to the wrapping element. The attribute name and value can be provided to the template tag as keyword arguments. Since attribute names can contain dashes, which are invalid in Python keyword argument names, any underscores in the argument name will be converted into dashes to form the HTML attribute name.
This feature is probably most useful for attaching data-*
attributes. For example, if interacting with a particular field should trigger an AJAX lookup of some description, the URL to use for that request could be stored in a data-url
attribute:
{% load djem %}
...
{% url 'verify-email' as verify_email_url %}
{% form_field registration_form.email data_url=verify_email_url %}
...
<div class="form-field form-field--required" data-url="/accounts/email/verify/">
<label for="id_email">Your email address:</label>
<input type="email" name="email" id="id_email" />
<div class="form-field__help">Don't worry, we won't share your email address with anyone.</div>
</div>
Something like the above could be used, for example, to keep hardcoded values (such as URLs) out of external JavaScript files.
Rendering in bulk¶
If you don’t need per-field customisation, such as additional CSS classes, extra HTML attributes, or custom checkbox labels, you can use form_field
in a loop:
{% load djem %}
...
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
{% for field in form.visible_fields %}
{% form_field field %}
{% endfor %}
...
Note
Note that, while form_field
will work for hidden fields, it is largely useless. Most of the features it provides are unnecessary for hidden fields.