=====
Forms
=====

The purpose of this package, of course, is the development forms in a simple
as possible way and still providing all the hooks to do customization at any
level as required by our real-world use cases. Thus, once the system is setup
with all its default registrations, it should be trivial to develop a new
form.

The strategy of this document is to provide the most common, and thus
simplest, case first and then demonstrate the available customization
options. In order to not overwhelm you with our set of well-chosen defaults,
all the default component registrations have been made prior to doing those
examples:

  >>> from z3c.form import testing
  >>> testing.setupFormDefaults()

Before we can start writing forms, we must have the content to work with:

  >>> import zope.interface
  >>> import zope.schema
  >>> class IPerson(zope.interface.Interface):
  ...
  ...     name = zope.schema.TextLine(
  ...         title=u'Name',
  ...         required=True)
  ...
  ...     gender = zope.schema.Choice(
  ...         title=u'Gender',
  ...         values=('male', 'female'),
  ...         required=False)
  ...
  ...     age = zope.schema.Int(
  ...         title=u'Age',
  ...         description=u"The person's age.",
  ...         min=0,
  ...         default=20,
  ...         required=False)

  >>> from zope.schema.fieldproperty import FieldProperty
  >>> class Person(object):
  ...     zope.interface.implements(IPerson)
  ...     name = FieldProperty(IPerson['name'])
  ...     gender = FieldProperty(IPerson['gender'])
  ...     age = FieldProperty(IPerson['age'])
  ...
  ...     def __init__(self, name, gender=None, age=None):
  ...         self.name = name
  ...         if gender:
  ...             self.gender = gender
  ...         if age:
  ...             self.age = age
  ...
  ...     def __repr__(self):
  ...         return '<%s %r>' %(self.__class__.__name__, self.name)

Okay, that should suffice for now.

What's next? Well, first things first. Let's create an add form for the
person. Since practice showed that the ``IAdding`` interface is overkill for
most projects, the default add form of ``z3c.form`` requires you to define the
creation and adding mechanism.

__Note__:

  If it is not done, ``NotImplementedError``'s are raised:

    >>> from z3c.form.testing import TestRequest
    >>> from z3c.form import form, field

    >>> abstract = form.AddForm(None, TestRequest())

    >>> abstract.create({})
    Traceback (most recent call last):
    ...
    NotImplementedError

    >>> abstract.add(1)
    Traceback (most recent call last):
    ...
    NotImplementedError

    >>> abstract.nextURL()
    Traceback (most recent call last):
    ...
    NotImplementedError


Thus let's now create a wroking add form:

  >>> class PersonAddForm(form.AddForm):
  ...
  ...     fields = field.Fields(IPerson)
  ...
  ...     def create(self, data):
  ...         return Person(**data)
  ...
  ...     def add(self, object):
  ...         self.context[object.name] = object
  ...
  ...     def nextURL(self):
  ...         return 'index.html'

This is as simple as it gets. We explicitely define the pieces that are
custom to every situation and let the default setup of the framework do the
rest. Yes, this looks extremely similar to ``zope.formlib`` and it is
intentional so, because we really like the simplicity of ``zope.formlib``'s
way of dealing with the common use cases.

During the test setup we also brought up a root folder already, so let's try
to add a new person object there. For this add form, of course, the context is
now the root folder:

  >>> request = TestRequest()
  >>> add = PersonAddForm(root, request)

Since forms are not necessarily pages -- in fact often they are not -- they
must not have a ``__call__`` method that does all the processing and rendering
at once. Instead, this form embraces the update/render pattern. Thus, we first
call the ``update()`` method.

  >>> add.update()

Actually a lot of things happen during this stage. Let me step through it one
by one pointing out the effects.


Find a widget manager and update it
-----------------------------------

The default widget manager knows to look for the ``fields`` attribute in the
form, since it implements ``IFieldsForm``:

  >>> from z3c.form import interfaces
  >>> interfaces.IFieldsForm.providedBy(add)
  True

The widget manager is then stored in the ``widgets`` attribute as promised by
the ``IForm`` interface:

  >>> add.widgets
  <z3c.form.field.FieldWidgets object at ...>

The widget manager will have three widgets, one for each field:

  >>> add.widgets.keys()
  ['name', 'gender', 'age']

When the widget manager updates itself, several sub-tasks are processed. The
manager goes through each field, trying to create a fully representative
widget for the field.

Field Availability
~~~~~~~~~~~~~~~~~~

Just because a field is requested in the field manager, does not mean that a
widget has to be created for the field. There are cases when a field
declaration might be ignored. The following reasons come to mind:

* No widget is created, if the data is not accessible in the content.
* A custom widget manager has been registered to particularly ignore a field.

In this simple case, all fields will be converted to widgets.

Widget Creation
~~~~~~~~~~~~~~~

So first it instantiates the widget with the field. During the process,
several pieces of information are transferred from the field to the widget:

  >>> age = add.widgets['age']

  # field.title -> age.label
  >>> age.label
  u'Age'

  # field.required -> age.required
  >>> age.required
  False

All these values can be overridden at later stages of the updating
process.

Widget Value
~~~~~~~~~~~~

The next step is to determine the the value that should be displayed by the
widget. There are three places where the value could come from:

* The field's default value.
* The request, in case a form has not been submitted or an error occurred.
* The context of the form, which represents the displayed content.

Since we are currently building an add form, the only the first two places are
effective. And since we do not have anything in the request, the value should
be the field's default value or be empty.

  >>> age.value
  u'20'

Since the widget only deals with output-ready values, the system also converts
the value using a data converter.

Widget Mode
~~~~~~~~~~~

Now the widget manager looks at the field to determine the widget mode -- in
other words whether the widget is a display or edit widget. In this case all
fields are input fields:

  >>> age.mode
  'input'

Deciding which mode to use, however, might not be a trivial operation. It
might depend on several factors:

* The permission to the content's data value
* The manual ``mode`` flag in the field
* The ``read_only`` flag in the schema field


Widget Attribute Values
~~~~~~~~~~~~~~~~~~~~~~~

As mentioned before, several widget attributes are optionally overridden when
the widget updates itself:

* label
* hint
* required
* mode

Since we have no customization components registered, all of those fields will
remain as set before.


Find an action manager, update and execute it
---------------------------------------------

After all widgets have been setup correctly, the actions are setup. By
default, the form machinery uses the button declaration on the form to create
its actions. For the add form, an add button is defined by default, so that we
did not need to create our own. Thus, there should be one action:

  >>> len(add.actions)
  1

The add button is an action and a widget at the same time:

  >>> addAction = add.actions['add']
  >>> addAction.title
  u'Add'
  >>> addAction.value
  u'Add'

After everything is setup, all pressed buttons are executed. Once a submitted
action is detected, a special action handler adapter is used to determine the
actions to take. Since the add button has not been pressed yet, no action
occurred.


Rendering the form
------------------

Once the update is complete we can render the form. Since we have not
specified a template yet, we have to do this now. We have prepared a small and
very simple template as part of this test:

  >>> import os
  >>> from zope.app.pagetemplate import viewpagetemplatefile
  >>> from z3c.form import tests
  >>> def addTemplate(form):
  ...     form.template = viewpagetemplatefile.BoundPageTemplate(
  ...         viewpagetemplatefile.ViewPageTemplateFile(
  ...             'simple_edit.pt', os.path.dirname(tests.__file__)), form)
  >>> addTemplate(add)

Let's now render the page:

  >>> print add.render()
  <html>
    <body>
      <form action=".">
        <div class="row">
          <label for="form.widgets.name">Name</label>
          <input type="text" id="form.widgets.name" name="form.widgets.name"
                 class="textWidget" value="" />
        </div>
        <div class="row">
          <label for="form.widgets.gender">Gender</label>
          <select id="form.widgets.gender" name="form.widgets.gender:list"
                  class="selectWidget" size="1">
            <option id="form.widgets.gender.novalue"
                    value="--NOVALUE--">no value</option>
            <option id="form.widgets.gender.0" value="male">male</option>
            <option id="form.widgets.gender.1" value="female">female</option>
          </select>
          <input name="form.widgets.gender-empty-marker" type="hidden"
                 value="1" />
        </div>
        <div class="row">
          <label for="form.widgets.age">Age</label>
          <input type="text" id="form.widgets.age" name="form.widgets.age"
                 class="textWidget" value="20" />
        </div>
        <div class="action">
          <input type="submit" id="form.buttons.add" name="form.buttons.add"
                 class="submitWidget" value="Add" />
        </div>
      </form>
    </body>
  </html>


Submitting an add form successfully
-----------------------------------

Let's now fill the request with all the right values so that upon submitting
the form with the "Add" button, the person should be added to the root folder:

  >>> request = TestRequest(form={
  ...     'form.widgets.name': u'Stephan Richter',
  ...     'form.widgets.gender': ['male'],
  ...     'form.widgets.age': u'20',
  ...     'form.buttons.add': u'Add'}
  ...     )

  >>> add = PersonAddForm(root, request)
  >>> add.update()

  >>> sorted(root)
  [u'Stephan Richter']
  >>> stephan = root[u'Stephan Richter']
  >>> stephan.name
  u'Stephan Richter'
  >>> stephan.gender
  'male'
  >>> stephan.age
  20


Submitting an add form with invalid data
----------------------------------------

Next we try to submit the add form with the required name missing. Thus, the
add form should not complete with the addition, but return with the add form
pointing out the error.

  >>> request = TestRequest(form={
  ...     'form.widgets.gender': ['male'],
  ...     'form.widgets.age': u'23',
  ...     'form.buttons.add': u'Add'}
  ...     )

  >>> add = PersonAddForm(root, request)
  >>> add.update()

The widget manager and the widget causing the error should have an error
message:

  >>> [error for error in add.widgets.errors]
  [<ErrorViewSnippet for RequiredMissing>]

  >>> add.widgets['name'].error
  <ErrorViewSnippet for RequiredMissing>

Let's now render the form:

  >>> from z3c.form import testing
  >>> add.template = viewpagetemplatefile.BoundPageTemplate(
  ...     viewpagetemplatefile.ViewPageTemplateFile(
  ...         testing.getPath('../tests/simple_edit.pt'), ''), add)
  >>> print add.render()
  <html>
    <body>
      <i>There were some errors.</i>
      <ul>
        <li>
          Name: <div class="error">Required input is missing.</div>
        </li>
      </ul>
      <form action=".">
        <div class="row">
          <b><div class="error">Required input is missing.</div>
          </b><label for="form.widgets.name">Name</label>
          <input type="text" id="form.widgets.name" name="form.widgets.name"
                 class="textWidget" value="" />
        </div>
        <div class="row">
          <label for="form.widgets.gender">Gender</label>
          <select id="form.widgets.gender" name="form.widgets.gender:list"
                  class="selectWidget" size="1">
            <option id="form.widgets.gender.novalue"
                    value="--NOVALUE--">no value</option>
            <option id="form.widgets.gender.0" value="male"
                    selected="selected">male</option>
            <option id="form.widgets.gender.1" value="female">female</option>
          </select>
          <input name="form.widgets.gender-empty-marker" type="hidden"
                 value="1" />
        </div>
        <div class="row">
          <label for="form.widgets.age">Age</label>
          <input type="text" id="form.widgets.age" name="form.widgets.age"
                 class="textWidget" value="23" />
        </div>
        <div class="action">
          <input type="submit" id="form.buttons.add" name="form.buttons.add"
                 class="submitWidget" value="Add" />
        </div>
      </form>
    </body>
  </html>

Note that the values of the field are now extracted from the request.

Let's now also provide a negative age, which is not possible either:

  >>> request = TestRequest(form={
  ...     'form.widgets.gender': ['male'],
  ...     'form.widgets.age': u'-5',
  ...     'form.buttons.add': u'Add'}
  ...     )

  >>> add = PersonAddForm(root, request)
  >>> add.update()

  >>> [(view.widget.label, view) for view in add.widgets.errors]
  [(u'Name', <ErrorViewSnippet for RequiredMissing>),
   (u'Age', <ErrorViewSnippet for TooSmall>)]

But the errror message for a negative age is too generic:

  >>> print add.widgets['age'].error.render()
  <div class="error">Value is too small</div>

It would be better to say that negative values are disallowed. So let's
register a new error view snippet for the ``TooSmall`` error:

  >>> from z3c.form import error

  >>> class TooSmallView(error.ErrorViewSnippet):
  ...     zope.component.adapts(
  ...         zope.schema.interfaces.TooSmall, None, None, None, None, None)
  ...
  ...     def update(self):
  ...         super(TooSmallView, self).update()
  ...         if self.field.min == 0:
  ...             self.message = u'The value cannot be a negative number.'

  >>> zope.component.provideAdapter(TooSmallView)

  >>> add = PersonAddForm(root, request)
  >>> add.update()
  >>> print add.widgets['age'].error.render()
  <div class="error">The value cannot be a negative number.</div>


Additional Form Attributes and API
----------------------------------

Since we are talking about HTML forms here, add and edit forms support all
relevant FORM element attributes as attributes on the class.

  >>> add.method
  'post'
  >>> add.enctype
  'multipart/form-data'
  >>> add.acceptCharset
  >>> add.accept

The ``action`` attribute is computed. By default it is the current URL:

  >>> add.action
  'http://127.0.0.1'

The name is also computed. By default it takes the prefix and removes any
trailing ".".

  >>> add.name
  'form'

The template can then use those attributes, if it likes to. Since we are
talking about templates, in this example we set the template manual, but if no
template is specified, the system tries to find an adapter. Initially, there
is no adapter, so rendering the form fails:

  >>> add.template = None
  >>> add.render()
  Traceback (most recent call last):
  ...
  ComponentLookupError: ((...), <InterfaceClass ...IPageTemplate>, u'')

The form module provides a simple component to create template factories:

  >>> factory = form.FormTemplateFactory(
  ...     testing.getPath('../tests/simple_edit.pt'), form=PersonAddForm)
  >>> zope.component.provideAdapter(factory)

Now the factory will be used to provide a template:

  >>> print add.render()
  <html>
  ...
  </html>

Since a form can also be used as a page itself, it is callable:

  >>> print add()
  <html>
  ...
  </html>


Changing Widget Attribute Values
--------------------------------

It frequently happens that a customer comes along and wants to slightly or
totally change some of the text shown in forms or make optional fields
required. Until now this always meant to implement a custom schema for this
client. With the z3c.form framework all attributes -- for which it is sensible
to replace a value without touching the code -- are customizable via an
attribute value adapter.

So let's change the label of the name widget from "Name" to "Full Name":

  >>> from z3c.form import widget
  >>> NameLabel = widget.StaticWidgetAttribute(
  ...     u'Full Name', field=IPerson['name'])
  >>> zope.component.provideAdapter(NameLabel, name='label')

When the form renders, the label should be changed:

  >>> add = PersonAddForm(root, TestRequest())
  >>> addTemplate(add)
  >>> add.update()
  >>> print add.render()
  <html>
  ...
  <div class="row">
    <label for="form.widgets.name">Full Name</label>
    <input type="text" id="form.widgets.name" name="form.widgets.name"
           class="textWidget" value="" />
  </div>
  ...


Adding a "Cancel" button
------------------------

Let's say a client requests that all add forms should have a "Cancel"
button. When the button is pressed, the user is forwarded to the next URL of
the add form. As always, the goal is to not touch the core implementation of
the code, but make those changes externally.

Adding a button/action is a little bit more involved than changing a value,
because you have to isnert the additional action and customize the action
handler. Based on your needs of flexibility, multiple approaches could be
chosen. Here we demonstrate the simplest one.

The first step is to create a custom action manager that always inserts a
cancel action:

  >>> from z3c.form import button
  >>> class AddActions(button.ButtonActions):
  ...     zope.component.adapts(
  ...         interfaces.IAddForm,
  ...         zope.interface.Interface,
  ...         zope.interface.Interface)
  ...
  ...     def update(self):
  ...         self.form.buttons = button.Buttons(
  ...             self.form.buttons,
  ...             button.Button('cancel', u'Cancel'))
  ...         super(AddActions, self).update()

After registering the new action manager,

  >>> zope.component.provideAdapter(AddActions)

the add form should display a cancel button:

  >>> add.update()
  >>> print add.render()
  <html>
  ...
  <div class="action">
    <input type="submit" id="form.buttons.add" name="form.buttons.add"
           class="submitWidget" value="Add" />
  </div>
  <div class="action">
    <input type="submit" id="form.buttons.cancel" name="form.buttons.cancel"
           class="submitWidget" value="Cancel" />
  </div>
  ...

But showing the button does not mean it does anything. So we also need a
custom action handler to handle the cancel action:

  >>> class AddActionHandler(button.ButtonActionHandler):
  ...     zope.component.adapts(
  ...         interfaces.IAddForm,
  ...         zope.interface.Interface,
  ...         zope.interface.Interface,
  ...         button.ButtonAction)
  ...
  ...     def __call__(self):
  ...         if self.action.name == 'form.buttons.cancel':
  ...            self.form._finishedAdd = True
  ...            return
  ...         super(AddActionHandler, self).__call__()

After registering the action handler,

  >>> zope.component.provideAdapter(AddActionHandler)

we can press the cancel button and we will be forwarded:

  >>> request = TestRequest(form={'form.buttons.cancel': u'Cancel'})

  >>> add = PersonAddForm(root, request)
  >>> addTemplate(add)
  >>> add.update()
  >>> add.render()
  ''

  >>> request.response.getStatus()
  302
  >>> request.response.getHeader('Location')
  'index.html'

Eventually, we might have action managers and handlers that are much more
powerful and some of the manual labor in this example would become
unnecessary.


Creating an Edit Form
---------------------

Now that we have exhaustively covered the customization possibilities of add
forms, let's create an edit form. Edit forms are even simpler than add forms,
since all actions are completely automatic:

  >>> class PersonEditForm(form.EditForm):
  ...
  ...     fields = field.Fields(IPerson)

We can use the created person from the successful addition above.

  >>> edit = PersonEditForm(root[u'Stephan Richter'], TestRequest())

After adding a template, we can look at the form:

  >>> addTemplate(edit)
  >>> edit.update()
  >>> print edit.render()
  <html>
    <body>
      <form action=".">
        <div class="row">
          <label for="form.widgets.name">Full Name</label>
          <input type="text" id="form.widgets.name" name="form.widgets.name"
                 class="textWidget" value="Stephan Richter" />
        </div>
        <div class="row">
          <label for="form.widgets.gender">Gender</label>
          <select id="form.widgets.gender" name="form.widgets.gender:list"
                  class="selectWidget" size="1">
            <option id="form.widgets.gender.novalue"
                    value="--NOVALUE--">no value</option>
            <option id="form.widgets.gender.0" value="male"
                    selected="selected">male</option>
            <option id="form.widgets.gender.1" value="female">female</option>
          </select>
          <input name="form.widgets.gender-empty-marker" type="hidden"
                 value="1" />
        </div>
        <div class="row">
          <label for="form.widgets.age">Age</label>
          <input type="text" id="form.widgets.age" name="form.widgets.age"
                 class="textWidget" value="20" />
        </div>
        <div class="action">
          <input type="submit" id="form.buttons.apply" name="form.buttons.apply"
                 class="submitWidget" value="Apply" />
        </div>
      </form>
    </body>
  </html>

As you can see, the data is being pulled in from the context for the edit
form. Next we will look at the behavior when submitting the form.


Failure Upon Submission of Edit Form
------------------------------------

Let's now submit the form having some invalid data.

  >>> request = TestRequest(form={
  ...     'form.widgets.name': u'Claudia Richter',
  ...     'form.widgets.gender': ['female'],
  ...     'form.widgets.age': u'-1',
  ...     'form.buttons.apply': u'Apply'}
  ...     )

  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
  >>> addTemplate(edit)
  >>> edit.update()
  >>> print edit.render()
  <html>
    <body>
      <i>There were some errors.</i>
      <ul>
        <li>
          Age: <div class="error">The value cannot be a negative number.</div>
        </li>
      </ul>
      <form action=".">
        <div class="row">
          <label for="form.widgets.name">Full Name</label>
          <input type="text" id="form.widgets.name" name="form.widgets.name"
                 class="textWidget" value="Claudia Richter" />
        </div>
        <div class="row">
          <label for="form.widgets.gender">Gender</label>
          <select id="form.widgets.gender" name="form.widgets.gender:list"
                  class="selectWidget" size="1">
            <option id="form.widgets.gender.novalue"
                    value="--NOVALUE--">no value</option>
            <option id="form.widgets.gender.0" value="male">male</option>
            <option id="form.widgets.gender.1" value="female"
                    selected="selected">female</option>
          </select>
          <input name="form.widgets.gender-empty-marker" type="hidden"
                 value="1" />
        </div>
        <div class="row">
          <b><div class="error">The value cannot be a negative number.</div>
          </b><label for="form.widgets.age">Age</label>
          <input type="text" id="form.widgets.age" name="form.widgets.age"
                 class="textWidget" value="-1" />
        </div>
        <div class="action">
          <input type="submit" id="form.buttons.apply" name="form.buttons.apply"
                 class="submitWidget" value="Apply" />
        </div>
      </form>
    </body>
  </html>


Successfully Editing Content
----------------------------

Let's now resubmit the form with valid data, so the data should be updated.

  >>> request = TestRequest(form={
  ...     'form.widgets.name': u'Claudia Richter',
  ...     'form.widgets.gender': ['female'],
  ...     'form.widgets.age': u'27',
  ...     'form.buttons.apply': u'Apply'}
  ...     )

  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
  >>> addTemplate(edit)
  >>> edit.update()
  >>> print edit.render()
  <html>
  ...
  <i>Data successfully updated.</i>
  ...

  >>> stephan = root[u'Stephan Richter']
  >>> stephan.name
  u'Claudia Richter'
  >>> stephan.gender
  'female'
  >>> stephan.age
  27


Successful Action with No Changes
---------------------------------

When submitting the form without any changes, the form will tell you so.

  >>> request = TestRequest(form={
  ...     'form.widgets.name': u'Claudia Richter',
  ...     'form.widgets.gender': ['female'],
  ...     'form.widgets.age': u'27',
  ...     'form.buttons.apply': u'Apply'}
  ...     )

  >>> edit = PersonEditForm(root[u'Stephan Richter'], request)
  >>> addTemplate(edit)
  >>> edit.update()
  >>> print edit.render()
  <html>
  ...
  <i>No changes were applied.</i>
  ...


Changing Status Messages
------------------------

Depending on the project, it is often desirable to change the status messages
to fit the application. In ``zope.formlib`` this was hard to do, since the
messages were burried within fairly complex methods that one did not want to
touch. In this package all those messages are exposed as form attributes.

There are three messages for the edit form:

* ``formErrorsMessage`` -- Indicates that there was an error occurred while
    applying the changes. This message is also available for the add form.

* ``successMessage`` -- The form data was successfully applied.

* ``noChangesMessage`` -- No changes were found in the form data.

Let's now change the ``noChangesMessage``:

  >>> edit.noChangesMessage = u'No changes were detected in the form data.'
  >>> edit.update()
  >>> print edit.render()
  <html>
  ...
  <i>No changes were detected in the form data.</i>
  ...

When even more flexibility is required within a project, one could also
implement these messages as properties looking up an attribute value. However,
we have found this to be a rare case.


Creating Edit Forms for Dictionaries
------------------------------------

Sometimes it is not desirable to edit a class instance that implements the
fields, but other types of object. A good example is the need to modify a
simple dictionaries, where the field names are the keys. To do that, a special
data manager for dictionaries is available:

  >>> from z3c.form import datamanager
  >>> zope.component.provideAdapter(datamanager.DictionaryField)

The only step the developer has to complete is to re-implement the form's
``getContent()`` method to return the dictionary:

  >>> personDict = {'name': u'Roger Ineichen'}
  >>> class PersonDictEditForm(PersonEditForm):
  ...     def getContent(self):
  ...         return personDict

We can now use the form as usual:

  >>> edit = PersonDictEditForm(None, TestRequest())
  >>> addTemplate(edit)
  >>> edit.update()
  >>> print edit.render()
  <html>
    <body>
      <form action=".">
        <div class="row">
          <label for="form.widgets.name">Full Name</label>
          <input type="text" id="form.widgets.name"
         name="form.widgets.name" class="textWidget"
         value="Roger Ineichen" />
        </div>
        <div class="row">
          <label for="form.widgets.gender">Gender</label>
          <select id="form.widgets.gender" name="form.widgets.gender:list" 
                  class="selectWidget" size="1">
            <option id="form.widgets.gender.novalue"
                    value="--NOVALUE--">no value</option>
            <option id="form.widgets.gender.0" value="male">male</option>
            <option id="form.widgets.gender.1" value="female">female</option>
          </select>
          <input name="form.widgets.gender-empty-marker" type="hidden"
                 value="1" />
        </div>
        <div class="row">
          <label for="form.widgets.age">Age</label>
          <input type="text" id="form.widgets.age"
                 name="form.widgets.age" class="textWidget" value="20" />
        </div>
        <div class="action">
          <input type="submit" id="form.buttons.apply"
                 name="form.buttons.apply" class="submitWidget"
                 value="Apply" />
        </div>
      </form>
    </body>
  </html>

Note that the name displayed in the form is identical to the one in the
dictionary. Let's now submit a form to ensure that the data is also written to
the dictionary:

  >>> request = TestRequest(form={
  ...     'form.widgets.name': u'Jesse Ineichen',
  ...     'form.widgets.gender': ['male'],
  ...     'form.widgets.age': u'5',
  ...     'form.buttons.apply': u'Apply'}
  ...     )
  >>> edit = PersonDictEditForm(None, request)
  >>> edit.update()

  >>> from zope.testing.doctestunit import pprint
  >>> pprint(personDict)
  {'age': 5,
   'gender': 'male',
   'name': u'Jesse Ineichen'}


Creating a Display Form
-----------------------

Creating a display form is simple; just instantiate, update and render it:

  >>> class PersonDisplayForm(form.DisplayForm):
  ...     fields = field.Fields(IPerson)
  ...     template = viewpagetemplatefile.ViewPageTemplateFile(
  ...         'simple_display.pt', os.path.dirname(tests.__file__))

  >>> display = PersonDisplayForm(stephan, TestRequest())
  >>> display.update()
  >>> print display.render()
  <html>
    <body>
      <div class="row">
        Claudia Richter
      </div>
      <div class="row">
        female
      </div>
      <div class="row">
        27
      </div>
    </body>
  </html>


Extending Forms
---------------

One very common use case is to extend forms. For example, you would like to
use the edit form and its defined "Apply" button, but add another button
yourself. Unfortunately, just inheriting the form is not enough, because the
new button and handler declarations will override the inherited ones. Let me
demonstrate the problem:

  >>> class BaseForm(form.Form):
  ...     fields = field.Fields(IPerson).select('name')
  ...
  ...     @button.buttonAndHandler(u'Apply')
  ...     def handleApply(self, form):
  ...         print 'success'

  >>> BaseForm.fields.keys()
  ['name']
  >>> BaseForm.buttons.keys()
  ['apply']
  >>> BaseForm.handlers
  <Handlers [<Handler for <Button 'apply' u'Apply'>>]>

Let's now derive a form from the base form:

  >>> class DerivedForm(BaseForm):
  ...     fields = field.Fields(IPerson).select('gender')
  ...
  ...     @button.buttonAndHandler(u'Cancel')
  ...     def handleCancel(self, form):
  ...         print 'cancel'

  >>> DerivedForm.fields.keys()
  ['gender']
  >>> DerivedForm.buttons.keys()
  ['cancel']
  >>> DerivedForm.handlers
  <Handlers [<Handler for <Button 'cancel' u'Cancel'>>]>

The obvious method to "inherit" the base form's information os to copy it
over:

  >>> class DerivedForm(BaseForm):
  ...     fields = BaseForm.fields.copy()
  ...     buttons = BaseForm.buttons.copy()
  ...     handlers = BaseForm.handlers.copy()
  ...
  ...     fields += field.Fields(IPerson).select('gender')
  ...
  ...     @button.buttonAndHandler(u'Cancel')
  ...     def handleCancel(self, form):
  ...         print 'cancel'

  >>> DerivedForm.fields.keys()
  ['name', 'gender']
  >>> DerivedForm.buttons.keys()
  ['apply', 'cancel']
  >>> DerivedForm.handlers
  <Handlers
      [<Handler for <Button 'apply' u'Apply'>>,
       <Handler for <Button 'cancel' u'Cancel'>>]>

But this is pretty clumsy. Instead, the ``form`` module provides a helper
method that will do the extending for you:

  >>> class DerivedForm(BaseForm):
  ...     form.extends(BaseForm)
  ...
  ...     fields += field.Fields(IPerson).select('gender')
  ...
  ...     @button.buttonAndHandler(u'Cancel')
  ...     def handleCancel(self, form):
  ...         print 'cancel'

  >>> DerivedForm.fields.keys()
  ['name', 'gender']
  >>> DerivedForm.buttons.keys()
  ['apply', 'cancel']
  >>> DerivedForm.handlers
  <Handlers
      [<Handler for <Button 'apply' u'Apply'>>,
       <Handler for <Button 'cancel' u'Cancel'>>]>

If you, for example do not want to extend the buttons and handlers, you can
turn that off:

  >>> class DerivedForm(BaseForm):
  ...     form.extends(BaseForm, ignoreButtons=True, ignoreHandlers=True)
  ...
  ...     fields += field.Fields(IPerson).select('gender')
  ...
  ...     @button.buttonAndHandler(u'Cancel')
  ...     def handleCancel(self, form):
  ...         print 'cancel'

  >>> DerivedForm.fields.keys()
  ['name', 'gender']
  >>> DerivedForm.buttons.keys()
  ['cancel']
  >>> DerivedForm.handlers
  <Handlers [<Handler for <Button 'cancel' u'Cancel'>>]>


Custom widget factories
-----------------------

Another important part of a form is, that we can use custom widgets. We can do
this in a form by defining a widget factory for a field. We can get the field
by get them from the fields collection e.g. fields['foo']. Which means, we can
define new widget factories by defining fields['foo'].widgetFactory = MyWidget.
Let's show a sample and define a custom widget:

  >>> from z3c.form.browser import text
  >>> class MyWidget(text.TextWidget):
  ...     """My new widget."""
  ...     css = u'MyCSS'

No we can define a field widget factory:

  >>> def MyFieldWidget(field, request):
  ...     """IFieldWidget factory for MyWidget."""
  ...     return widget.FieldWidget(field, MyWidget(request))

We register the MyWidget in a form like:

  >>> class MyEditForm(form.EditForm):
  ...
  ...     fields = field.Fields(IPerson)
  ...     fields['name'].widgetFactory = MyFieldWidget

We can see, that the custom widget get used in the rendered form:

  >>> myEdit = MyEditForm(root[u'Stephan Richter'], TestRequest())
  >>> addTemplate(myEdit)
  >>> myEdit.update()
  >>> print myEdit.render()
  ...
  <html...
  <input type="text" id="form.widgets.name"
         name="form.widgets.name" class="MyCSS"
         value="Claudia Richter" />
  ...
