Creating the form view 
======================

**Using our schema in a form**

To render our form, we need to create a view that uses a *z3c.form* base
class. The view is registered like any other, either in ZCML or, as we
will show here, by using convention-over-configuration ala `five.grok`_.
It is then configured with the schema to use for form fields, the label
(page title) and description (lead-in text) to show, and actions to
render as buttons.

Still in *order.py*, we add the following:

::

    class OrderForm(form.SchemaForm):
        grok.name('order-pizza')
        grok.require('zope2.View')
        grok.context(ISiteRoot)
        
        schema = IPizzaOrder
        ignoreContext = True
        
        label = _(u"Order your pizza")
        description = _(u"We will contact you to confirm your order and delivery.")
        
        def update(self):
            # disable Plone's editable border
            self.request.set('disable_border', True)
            
            # call the base class version - this is very important!
            super(OrderForm, self).update()
        
        @button.buttonAndHandler(_(u'Order'))
        def handleApply(self, action):
            data, errors = self.extractData()
            if errors:
                self.status = self.formErrorsMessage
                return
            
            # Handle order here. For now, just print it to the console. A more
            # realistic action would be to send the order to another system, send
            # an email, or similar
            
            print u"Order received:"
            print u"  Customer: ", data['name']
            print u"  Telephone:", data['telephone']
            print u"  Address:  ", data['address1']
            print u"            ", data['address2']
            print u"            ", data['postcode']
            print u"  Order:    ", ', '.join(data['orderItems'])
            print u""
            
            # Redirect back to the front page with a status message

            IStatusMessage(self.request).addStatusMessage(
                    _(u"Thank you for your order. We will contact you shortly"), 
                    "info"
                )
            
            contextURL = self.context.absolute_url()
            self.request.response.redirect(contextURL)
            
        @button.buttonAndHandler(_(u"Cancel"))
        def handleCancel(self, action):
            """User cancelled. Redirect back to the front page.
            """
            contextURL = self.context.absolute_url()
            self.request.response.redirect(contextURL)

Let’s go through this in some detail:

-  We derive our form view from one of the standard base classes in
   *plone.directives.form*. The *SchemaForm* is a *plone.autoform*-based
   form (so it configures the form fields from the schema automatically
   and takes schema hints into account), without any of the standard
   actions that can be found on more specialised base classes such as
   *SchemaAddForm* or *SchemaEditForm*. It basically mirrors the
   *z3c.form.form.Form* base class.
-  We then use the standard*five.grok* view directives to register the
   view: *grok.name()* gives it a friendly name (used as a path segment
   in the URL); *grok.context()* sets the type of context where the form
   is available (here, we make it available on the Plone site root,
   though any interface or class may be passed; to make the form
   available on any context, use *zope.interface.Interface* as the
   context); *grok.require()* specifies a permission which the user must
   have to be able to view the form (here, we use the standard
   *zope2.View* permission). See the views section in the `five.grok
   manual`_ for more detail.
-  Next, we specify the schema via the *schema* attribute. This is the
   equivalent of assigning the *fields* attribute to a *field.Fields()*
   instance, as you may have seen in documentation for “plain”
   *z3c.form. (*In fact, the "*fields = field.Fields(ISchema)*" pattern
   of working is supported if you use *plone.directives.form.Form* as a
   base class instead of *SchemaForm*, but you will then be unable to
   use form schema hints in the schema itself - more on this later.)
-  We set *ignoreContext* to *True*. This tells *z3c.form* not to
   attempt to read the current value of any of the form fields from the
   context. The default behaviour is to attempt to adapt the context
   (the Plone site root in this case) to the schema interface and read
   the schema attribute value from this adapter when first populating
   the form. This makes sense for edit forms and things like control
   panels, but not for a standalone form like this.
-  We then set a *label* and *description* for the form. In the standard
   form template, these are rendered as a page header and lead-in text,
   respectively.
-  We override the *update()* method to set the *disable\_border*
   request variable. This hides the editable border when rendering the
   form. We then call the base class version of *update()*. This is
   crucial for the form to work! *update()* is a good place to perform
   any pre-work before the form machinery kicks in (before calling the
   base class version) or post-processing afterwards (after calling the
   base class version). See the section on the form rendering lifecycle
   later in this manual for the gory details.
-  Finally, we define two actions, using the
   *@button.buttonAndHandler()* decorator. Each action is rendered as a
   button (in order). The argument is a (translated) string that will be
   used as a button label. The decorated handler function will be called
   when the button is clicked.

For the purposes of this test, the actual work we do with the main
handler is relatively contrived. However, the patterns are generally
applicable.

The second button (cancel) is the simpler of the two. It performs no
validation and simply redirects to the context’s default view, i.e. the
portal front page in this case.

The first button actually extracts the data from the form, using
*self.extractData()*. This returns a tuple of the form data, which has
been converted to the field’s underlying type by each widget (thus, the
value corresponding to the *Set* field contains a *set*) and any errors.
If there are errors, we abort, setting *self.status* to confer an error
message at the top of the page. Otherwise, we use the form data (here
just printing the output to the console - you need to run Zope in
foreground mode to see these messages), add a cookie-tracked status
message (so that it can appear on the next page) and redirect the user
to the context’s default view. In this case, that means the portal front
page.

.. _five.grok manual: /products/dexterity/documentation/manual/five.grok
.. _five.grok: /products/dexterity/documentation/manual/five.grok
