Personal tools
You are here: Home Documentation Tutorials Working with Forms in Grok Creating a Form

Creating a Form

How to create a simple Form
A walkthrough of the basics of automatically generating HTML forms using Grok, as well as a discussion of a few more advanced Form manipulations.
Page 4 of 9.

Grok provides four types of Form components, grok.Form, grok.AddForm, grok.EditForm and grok.DisplayForm. The latter three are all specializations of the basic grok.Form class and the grok.Form class itself is a specialization of the grok.View class. This means that all of the methods and attributes available in grok.View are available in any Grok Form. You can rely on self.context to represent the model object that the form is acting upon, and self.request to contain the current HTTP request object.

This also means that in simple cases, it's not necessary to have both a View class and a Form class, these can both be neatly tied into a single Form class. For more complex use cases it is possible to instantiate and work with several Forms from within one View, this is described later in the tutorial.

We'll now create the "Mammoth manager", a simple Grok application that lets us maintain data about a collection of Mammoths. Our starting application looks like:

import grok
from zope import schema
from zope import interface

class IMammoth(interface.Interface):
    "Describes a Mammoth"
    furryness = schema.Text(
        title = u'Description of the fur.',
        description = u"""
This field is primarily used by cavemen to aid in sorting and processing
the mammoths in the spring during fur harvesting season.""",
        required = True,
        default = u'Brown. Average quality.',
    )
    weight = schema.Int(
        title = u'Weight',
        description = u'Measured in Kilograms',
        required = True,
    )
    owner = schema.TextLine(
        title = u'Name of the owner.',
        description = u'Kept as a pet unless the owner is very hungry.',
        required = False,
    )

class MammothApplication(grok.Application, grok.Container):
    """World's greatest Mammoth manager web application."""

class Mammoth(grok.Model):
    grok.context(MammothApplication)
    grok.implements(IMammoth)

    furryness = u''
    weight = 0
    owner = u''

class MammothForm(grok.Form):
    grok.context(MammothApplication)
    grok.name('index')
    form_fields = grok.AutoFields(Mammoth)

You should already be familiar with the basics of a simple Grok application, the interesting part is the MammothForm class. For now we are declaring that this Form is named index so that it the default view for our application is this simple Form.

We've added one new line to the Form:

form_fields = grok.AutoFields(Mammoth)

The form_fields attribute in a form must be an object that implements IFormFields. The easiest way to generate a list of form fields that conforms to the IFormFields interface is to use the convenience function grok.AutoFields() upon a model object. This will generate form fields from all schema fields that the object provides.

Often you don't want to use every schema field in a model object. You can use the select() and omit() methods to choose just the fields you want:

# ask for fields by name
form_fields = grok.AutoFields(Mammoth).select('furryness','owner')

# or choose all fields and remove the unwanted ones
form_fields = grok.AutoFields(Mammoth).omit('weight')

When we view our Grok application, the MammothForm view will be rendered and it looks like this:

mammothform.jpg

This is a start, but there is a problem with this form. It doesn't have a submit button! Let's extend MammothForm so that it can handle the creation of a new Mammoth.

class MammothForm(grok.AddForm):
    grok.context(MammothApplication)
    grok.name('index')
    form_fields = grok.AutoFields(Mammoth)
    label = "Let's Make a Mammoth"

    @grok.action('Add Mammoth')
    def add(self, **data):
        mammoth = Mammoth()
        self.applyData(mammoth, **data)
        import datetime
        name = str(datetime.datetime.now()).replace(' ','-')
        self.context[name] = mammoth
        return self.redirect(self.url(self.context[name]))

class MammothView(grok.View):
    "Display a Mammoth"
    grok.context(Mammoth)
    grok.name('index')

    def render(self):
        return """
<html><body>
    <p><b>Furryness:</b> %s</p>
    <p><b>Weight:</b> %s kilograms</p>
    <p><b>Owner:</b> %s</p>
</html></body>""" % (
            self.context.furryness,
            self.context.weight,
            self.context.owner,
        )

What's changed? The base class of form to is now an grok.AddForm to indicate that this form is intended for the creation of new Mammoth objects. We have also given our form a label attribute, this value will be displayed at the top of our form to improve the user-interface. We have created an add method that will be called when the form is submitted. The button in our form is automatically rendered and wire it up to the add method by using the grok.action decorator. This decorator takes a single argument which will be used as the name of the button.

Our new form, once filled out and ready for submission, looks like this:

makemammoth.jpg

The add method we created uses another feature of forms, the applyData() method. This is a convience method for automatically takign data submitted by the form in the request and setting it as corresponding attributes in the the data object. Calling this method also sends out a grok.IObjectMovedEvent if any of the data has changed.