﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
17301	Defining fieldsets in a form class	msiedlarek	nobody	"'''Motivation'''

For a large form grouping of fields is not just the pretty-rendering and template-related issue and I believe there should be way to logically group fields a form class definition. It would also be convenient to have Django automatically render those group of fields, just as easy as it is now to do `{{ form.as_p }}`.

'''Idea'''

The idea is to use existing and well-known conventions. Example:

{{{#!python
class PersonForm(forms.Form):
    home_phone = CharField()
    cell_phone = CharField()
    first_name = CharField()
    last_name = CharField()

    class Meta:
        fieldsets = (
            (None, {
                'fields': ('first_name', 'last_name'),
            }),
            (""Phone numbers"", {
                'fields': ('cell_phone', 'home_phone'),
            }),
        )
}}}

Note usage of conventions already known to many users -- `Meta` class for options (used in models and `ModelForm` already) and `fieldsets` tuple, just like one in `ModelAdmin` options.

The idea is also not to break anything already working meaning being backward compatible. Forms without fieldsets explicitly defined by user should render normally.

Having a form defined as above user can easily use it in a template. Example:

{{{
<form action="""" method=""post"">
    {{ form.as_table }}
    <input type=""submit"" value=""Send"" />
</form>
}}}

Renders to:

{{{
<form action="""" method=""post"">
    <fieldset>
        <table>
            <tr>
                <th><label for=""id_first_name"">First name:</label></th>
                <td><input type=""text"" name=""first_name"" id=""id_first_name"" /></td>
            </tr>
            <tr>
                <th><label for=""id_last_name"">Last name:</label></th>
                <td><input type=""text"" name=""last_name"" id=""id_last_name"" /></td>
            </tr>
        </table>
    </fieldset>
    <fieldset>
        <legend>Phone numbers</legend>
        <table>
            <tr>
                <th><label for=""id_cell_phone"">Cell phone:</label></th>
                <td><input type=""text"" name=""cell_phone"" id=""id_cell_phone"" /></td>
            </tr>
            <tr>
                <th><label for=""id_home_phone"">Home phone:</label></th>
                <td><input type=""text"" name=""home_phone"" id=""id_home_phone"" /></td>
            </tr>
        </table>
    </fieldset>
    <input type=""submit"" value=""Send"" />
</form>
}}}

Which I believe is a good default behavior. Other examples:

{{{
<form action="""" method=""post"">
    {% for fieldset in form.fieldsets %}
        <fieldset>
            {{ fieldset.legend_tag }}
            <table>
                {{ fieldset.as_table }}
            </table>
        </fieldset>
    {% endfor %}
    <input type=""submit"" value=""Send"" />
</form>
}}}

{{{
<form action="""" method=""post"">
    {% for fieldset in form.fieldsets %}
        <fieldset>
            {{ fieldset.legend_tag }}
            <ul>
                {% for field in fieldset %}
                    <li>
                        {{ field.label_tag }}
                        {{ field }}
                    </li>
                {% endfor %}
            </ul>
        </fieldset>
    {% endfor %}
    <input type=""submit"" value=""Send"" />
</form>
}}}

{{{
<form action="""" method=""post"">
    {% for fieldset in form.fieldsets %}
        <fieldset>
            {{ fieldset.legend_tag }}
            <ul>
                {% for field in fieldset.visible_fields %}
                    <li>
                        {{ field.label_tag }}
                        {{ field }}
                    </li>
                {% endfor %}
                <li class=""super-hidden"">
                    {% for field in fieldset.hidden_fields %}
                        {{ field }}
                    {% endfor %}
                </li>
            </ul>
        </fieldset>
    {% endfor %}
    <input type=""submit"" value=""Send"" />
</form>
}}}

'''Implementation'''

Yeah, there's a patch. Short description of implementation for those afraid of reading the diff:

1. Creating `BaseFormOptions` class, similar to this already present in `ModelForm` class (for defining model for form). `ModelFormOptions` now subclasses it. `BaseFormMetaclass` now handles options-related stuff. (note that this behavior may be used in future for other form options)
2. Separating all rendering from `Form` class to a new one - `Fieldset`. Form not having any fieldsets defined uses the single, dummy fieldset for all its fields.
3. No editing any of already existing tests. Everything should be backward compatible.
4. Providing some tests for new behavior - form options and fieldsets.
5. Providing some documentation. Unfortunately my English skills are, as you see, not that impressive, so it definitely need some touch. Now it's more like set of examples.

'''Patch'''

Patch is attached.  You can read it on Trac here or on my Django github fork: https://github.com/mikoskay/django/compare/master...form-fieldsets"	New feature	closed	Forms	dev	Normal	duplicate	form, forms, fieldsets	msiedlarek riccardo.magliocchetti@…	Accepted	1	1	0	0	0	0
