[Fixed]-Django form field grouping


Any logical way to group fields would work… say you have a method on your form that returns form fields that you explicitly group?

To save typing, perhaps a certain field prefix naming scheme?

class MyForm(forms.Form):
    group1_field = forms.CharField()
    group1_field = forms.CharField()
    group2_field = forms.CharField()
    group2_field = forms.CharField()

   def group1(self):
        return [self[name] for name in filter(lambda x: x.startswith('group1_'), self.fields.values()]

Perhaps set an attribute on the field you can filter by?

class MyForm(forms.Form):
    field = forms.CharField()
    field.group = 1

    field2 = forms.CharField()
    field2.group = 2

    def group1(self):
        return filter(lambda x: x.group == 1, self.fields.values())

    def group2(self):
        return filter(lambda x: x.group == 2, self.fields.values())

You could also use the regroup tag if you set these attributes.

{% regroup form.fields by group as field_group %}
{% for group in field_group %}
<div class="group_{{ group.grouper }}">
  {% for field in group.list %}
    {{ field }}
  {% endfor %}
{% endfor %}


I finally was able to bring @Yuji’Tomita’Tomitas regroup-template-tag-solution to work (see comments in @Yuji’Tomita’Tomitas answer to understand difficulties). I think this is really a nice and easy way to perfom grouping of fields!

The solution was to regroup via group attribute of field accessing the field attribute of returned BoundFields. Minimal example:

In forms.py :

class TestForm(Form):
    a = IntegerField()
    a.group = 1
    b = IntegerField()
    b.group = 1
    c = IntegerField()
    c.group = 2
    d = IntegerField()
    d.group = 2

In template:

  {% csrf_token %}
  {% regroup form by field.group as field_groups %}
  {% for field_group in field_groups %}
    {% for field in field_group.list %}
    {% endfor %}
  {% endfor %}


Here’s a relevant SO question: Django and Fieldsets on Modelform, though this seems a bit overkill for what I’m looking to accomplish. Also, here’s one possible hack, although I’m curious to hear how Django experts solve this problem.

(0) Define a python fieldset object that is iterable so we can iterate over it in a django template:

from django.forms.forms import BoundField

class FieldSet(object):
    def __init__(self,form,fields,legend='',cls=None):
        self.form = form
        self.legend = legend
        self.fields = fields
        self.cls = cls

    def __iter__(self):
        for name in self.fields:
            field = self.form.fields[name]
            yield BoundField(self.form, field, name)

(1) In the view use:

fieldsets = (FieldSet(form_object, ('field_name1','field_name2'),
                        legend='Div env 1',
             FieldSet(form_object, ('field_name3','field_name4'), 
                        legend="Div env 2",

return render_to_response('my_form.html',
                          {'form': form_object,'fieldsets':fieldsets},

(2) Now in the template do:

{% for set in fieldsets %}
    <fieldset{% if set.cls %} class="{{ set.cls }}"{% endif %}>
      <legend>{{ set.legend }}</legend>
      {% for field in set %}
          {{ field.label}} : {{ field }}
      {% endfor %}
{% endfor %}

Note that it is possible to replace the fieldset tag with a div tag to address my specific question.

+++ Much of this answer extracted from this blog post by Michael Kowalchik. +++

Leave a comment