Displaying fieldsets with Jinja2 and Django forms, the quick way

By on April 30th, 2010 in Python Leave a Comment

We had two problems to solve: we wanted to use fieldsets with Django forms, and the follow-up issue, making that custom rendering Jinja2-compatible. There are plenty of awesome libraries/snippets to display forms as fieldsets, including django-form-utils.

I wanted a quick and dirty and method of doing this.

So here it is: step 1, add a fieldsets variable to your form class (does not need to be named fieldsets as long as the template macro corresponds) and define your fieldsets in a tuple of dictionaries (pick a data structure, that’s just one option), as such:

1
2
3
4
5
6
7
8
9
10
class InfoForm(forms.Form):
   fieldsets = ( dict(legend='Account info',fields=('email', 'password')),
                  dict(legend='Personal info',fields=('first_name', 'last_name')))
    # Account info
    email = forms.EmailField(label='Email', max_length=75)
    password = forms.CharField(label='Password', widget=forms.PasswordInput)
 
    # Personal info
    first_name = forms.CharField(label='First Name')
    last_name = forms.CharField(label='Last Name')

Now write the code to integrate this into your template. I created a Jinja2 macro for reusability. (Side note: I have assigned “##” to mean a comment in Jinja2 and “%” to denote a block or macro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## Adds in a form divided into fieldsets. Expects fieldsets to be defined as dictionaries 
## within a tuple at the class level named fieldsets.
% macro fieldset_form(form)
    % for fset in form.fieldsets:
        <fieldset>
            <legend>{{ fset.legend }}</legend>
            % for field in fset.fields:
                ## Get the bound form field, which can be transformed into HTML.
                % set bf = form[field]
                <p>
                {{ bf.errors }}
                {% if bf.label %}{{ bf.label }}{% endif %}
                {{ bf }}
                </p>
            % endfor
        </fieldset>
    % endfor
% endmacro

The important bit of this is that it is the bound field form which evaluates to the HTML input tag, not the unbound field, and that’s why we’ve stored the field name (as a string) instead of the field itself, in our fieldsets variable. Django can be kind of wacky.

To use this macro in our templates, we simply import the macro if it is in a different file and call it with the form object passed in to the template by our view.

1
2
3
4
<form action="/something" method="post">
   {{ fieldset_form(form) }}
  <input type="submit" name="submit" value="submit"/>
</form>

There you have it; hope this helps anyone struggling with the same problem!