Rendering form fields as group in Django
Field groups in Django
A welcome addition in Django 5.0, the concept of field groups makes a lot easier to customize the layout of our Django forms.
In the past, to take control of field layout, developers had to render field labels, help text, errors and fields one by one (or resort to use an external library).
Field group solve that, as we will see in a moment.
In this brief post I base my examples over a Django library
app, with a model named Book
. You can look up the structure in this repo.
Customizing the form renderer
As a first step to opt-in into Django form field groups, we need to customize the form rendered. This can be done by creating your custom renderer in a file named renderers.py
, ideally in a common
app in your project.
The custom form rendered must inherit from TemplatesSetting
in order to make the field template swappable:
from django.forms.renderers import TemplatesSetting
class FormRenderer(TemplatesSetting):
field_template_name = "common/field.html"
The renderer can then be used in settings.py
:
INSTALLED_APPS = [
...
# Remember always to include django.forms
'django.forms',
'common'
]
FORM_RENDERER = 'common.renderers.FormRenderer'
Pay attention: in order to be able to customize the field group template in the following section, django.forms
must be in our INSTALLED_APPS
!
Customizing the form field group template project-wide
Now that we declared our custom form renderer, we can proceed to customize the form field template by creating a new file named field.html
in common/templates/common
:
{% if field.use_fieldset %}
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
{% if field.label %}{{ field.legend_tag }}{% endif %}
{% else %}
{% if field.label %}{{ field.label_tag }}{% endif %}
{% endif %}
{% if field.help_text %}<div class="helptext"{% if field.auto_id %} id="{{ field.auto_id }}_helptext"{% endif %}>{{ field.help_text|safe }}</div>{% endif %}
{{ field }}
{{ field.errors }}
{% if field.use_fieldset %}</fieldset>{% endif %}
This template is taken from Django itself.
You can now enrich it with any desired change, or leave it as it is.
Let's now create the view to handle the form.
Rendering fields: the create view
With the template in place, we can now create a Django view to display the form. I use a CreateView
in this example, which generates its form automatically. If you need more customization over widgets or over the validation, you can declare your own ModelForm
.
Here's the view to create books:
class BookCreateView(CreateView):
model = Book
fields = ["title", "authors"]
success_url = "/"
Note: your custom field template will work well even with a regular Django Form
.
With the view and the form in place, we can render the form in our template.
Rendering fields: the form template
We are now ready to render our Django form in a template. Here it is:
{% extends "library/base.html" %}
{% block content %}
<form novalidate method="post">
{% csrf_token %}
<div class="form-group">
{{ form.title.as_field_group }}
</div>
<div class="form-group">
{{ form.authors.as_field_group }}
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
{% endblock %}
As you can see, we no longer need to call field attributes one by one to group the field into a containing HTML element.
We can just call form.field_name.as_field_group
:
...
<div class="form-group">
{{ form.title.as_field_group }}
</div>
...
Isn't that almost magic?
Calling form.field_name.as_field_group
will render the label, any help text, the field error list, and the field itself.
Here's the HTML for the title field after a form submit:
<div class="form-group">
<label for="id_title">Title:</label>
<ul class="errorlist"><li>This field is required.</li></ul>
<input type="text" name="title" maxlength="100" required="" aria-invalid="true" id="id_title">
</div>
Note: if you don't see any effect after customizing the field template in your project, check that django.forms
is declared in INSTALLED_APPS
.
Reusable form templates
Keep in mind that you can not only customize the field template, but also take control of the form template, if you need to do so. Check reusable form templates for more.
Conclusion
Reusable form field groups in Django make a lot easier to take control of form field rendering without reaching for external libraries.
Available since Django 5.0 form fields can render as a group, and have their template overridden in different ways.
This can happen at various levels as described also in the documentation.
Thanks for reading!