#17301: Defining fieldsets in a form class
-----------------------------+------------------------------------
     Reporter:  msiedlarek   |      Owner:  nobody
         Type:  New feature  |     Status:  new
    Component:  Forms        |    Version:  SVN
     Severity:  Normal       |   Keywords:  form, forms, fieldsets
 Triage Stage:  Unreviewed   |  Has patch:  1
Easy pickings:  0            |      UI/UX:  0
-----------------------------+------------------------------------
 '''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

-- 
Ticket URL: <https://code.djangoproject.com/ticket/17301>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to