12. Fieldsets¶
In HTML the <form>
-element is just a data-abstraction layer. It has no display properties and is
not intended to be styled or annotated. Its purpose is to group one or more input fields, in order
to submit their gathered input data to the server altogether.
On the other side, we might want to visually group those input fields and optionally add a legend
tag to create a caption for the form. We also might want to group related input fields visually by
surrounding them with a border. For this purpose the HTML standard defines the <fieldset>
tag.
Django itself does not offer any abstraction for this HTML tag. If one wants to use it, this has to
be done on the template level when rendering the form.
To fill this gap, django-formset introduces a Python class to handle the <fieldset>
-element.
From a technical point of view, a fieldset behaves exactly like a single form and in HTML it always
must be wrapped inside a <form>
-element. If we want to use more than one fieldset, then we have
to group them using Form Collections, just as we would do with normal forms.
A fieldset accepts the optional string attribute legend
. This then is rendered as a
<legend>
-element inside the <fieldset>
. A fieldset also accepts the optional string
attribute help_text
. This is rendered as a muted <p>
-element after the last field but inside
that fieldset.
Another purpose of using fieldsets, apart from adding a border and a legend to a form, is to use Conditional Field and Fieldset Expressions. This allows us to hide or disable the whole fieldset depending on the context of other fields.
12.1. Example¶
In this example we use two forms, a fieldset to ask for some customer’s personal data and a form
with just one Boolean field, both nested in a FormCollection
. Remember, a Fieldset
behaves
exactly as a Form
instance and can be used as a replacement, although with additional styling
possibilities. Here we group those two forms into one collection named CustomerCollection
to
build one submittable entity.
from formset.renderers.bootstrap import FormRenderer
from django.forms import fields, forms
from formset.fieldset import Fieldset
from formset.collection import FormCollection
from formset.views import FormCollectionView
class CustomerForm(Fieldset):
legend = "Customer"
hide_condition = 'register.no_customer'
recipient = fields.CharField(label="Recipient", required=False)
address = fields.CharField(label="Address", required=False)
class RegisterForm(forms.Form):
no_customer = fields.BooleanField(
label="I'm not a customer",
required=False,
)
class CustomerCollection(FormCollection):
customer = CustomerForm()
register = RegisterForm()
default_renderer = FormRenderer(
field_css_classes='mb-3',
fieldset_css_classes='border rounded p-3 mb-3',
)
class CustomerView(FormCollectionView):
collection_class = CustomerCollection
template_name = "form-collection.html"
success_url = "/success"
Note
Bootstrap hides the border of fieldsets. Therefore in this example, we added a default renderer, to set the proper CSS classes for the given fieldset.
The interesting part of this collection is that we can hide the entire fieldset by clicking on the
checkbox named “I’m not a customer”. This means that by using conditionals, we can dynamically
adjust the visibility of a complete fieldset. In this example we add
hide_condition = 'register.no_customer'
to the class CustomerForm
. Whenever someone clicks
onto that checkbox, the whole upper fieldset is hidden.
Remember to make the fields in the fieldset optional. Otherwise if the fieldset is hidden, the form
submission will fail without being able to give feedback which fields are missing. If you need a
specific validation logic, add it to the form’s clean()
-method.