Introduction to Z3C Forms

Location:Plone Conference 2008, Washington, DC
Date: October 10, 2008
Presenter:Stephan Richter, Keas Inc.

Mastering the digital bureaucracy perferctly.

Getting Started

Just Kidding

Juuuussttt kidding!

Your Choice

Today you have the choice between:

  1. a typical talk about z3c.form and its extensions

or

  1. a tutorial (as promised in the brochure) building a small z3c.form app

Form Automation

Form automation is an extremely viable part in making Zope 3 a high-productivity environment. In fact, the implementation presented in this session is already the third generation of form machinery. Older versions are still available in zope.app.form and zope.formlib but is not used anymore other than in legacy code.

Forms

If the UI requirements are consistent enough, it is sufficient to use one generic template for all or most forms. In my experience, however, this has not been the case and I have always ended up writing templates for each form. With enough helper functions this is commonly a quick task and does not represent a significant amount of the development time.

Fields

Creating a Schema

Selecting Fields

Widgets

In contrast to previous widget implementations, the z3c.form package's widgets are very simple and are only respsonsible for ensuring the correct rendering in the output media, in our case most often HTML forms.

Widget Components Graph

Widget Component Structure

Buttons

Actions

Declaring Buttons (1)

Declaring Buttons (2)

Form Classes

To keep matters simple within the form package, those forms do not directly proivde the correct APIs for advanced UI patterns such as viewlets and view templates. However, the overhead to make them work well with those pattersn is very small and a common base class is quickly developed.

Form Components Graph

Form Component Structure

Hello World Content

import persistent
import zope.location
from zope.schema.fieldproperty import FieldProperty
from training.z3cform import interfaces

class HelloWorldMessage(zope.location.Location, persistent.Persistent):
    
zope.interface.implements(interfaces.IHelloWorldMessage)

    
who = FieldProperty(interfaces.IHelloWorldMessage['who'])
    
when = FieldProperty(interfaces.IHelloWorldMessage['when'])
    
what = FieldProperty(interfaces.IHelloWorldMessage['what'])

    
def __init__(self, who, when, what):
        
self.who = who
        
self.when = when
        
self.what = what

Hooking up the Content

Add Forms

Message Add Form (1)

from zope.traversing.browser import absoluteURL
from z3c.form import form, field
from training.form import interfaces, message

class HelloWorldAddForm(form.AddForm):
    
label = u'Hello World Message Add Form'
    
fields = field.Fields(interfaces.IHelloWorldMessage)
    
template = pagetemplate.ViewPageTemplateFile('form.pt')

    
def create(self, data):
        
return message.HelloWorldMessage(**data)

    
def add(self, object):
        
count = 0
        
while 'helloworld-%i' %count in self.context:
            
count += 1;
        
self._name = 'helloworld-%i' %count
        
self.context[self._name] = object
        
return object

    
def nextURL(self):
        
return absoluteURL(self.context[self._name], self.request)

As you can see, we only need to implement three simple methods. The create() method's job is to create a valid component from the data of the form and return it. Next, the add(object) method is responsible for adding the object -- usually to a contained. Part of its contract is that it chooses a valid name. The nextURL() method simply returns a path to redirect to.

Message Add Form (2)

Message Add Form (3)

<html>
  
<head>
    
<link type="text/css" rel="stylesheet"
          
href="somestyle.css" media="all"
          
tal:attributes="href context/++resource++div-form.css" />
  
</head>
  
<body>
    
<div metal:use-macro="macro:form">
      Form goes here.
    
</div>
  
</body>
</html>

z3c.form was written with pagelets in mind. Since we want to keep the example as self-contained as possible, we are not using pagelets, which means that we have to define our own form templates. The div-form.css CSS resource and the form macro are registered in the IDivFormLayer layer.

Custom Widget Value

Display Forms

While writing display forms every time you want to present a value might seem tedious at first, but it is worth the discipline. Otherwise you will eventually have unwanted presentation bugs. Common base classes can easily be created to make this a very trivial part of the development.

Message Display (1)

Message Display (2)

Message Display (3)

Edit Forms

Edit forms are great, because their scope is limited and they can be easily used within other presentation components, allowing you to implement many interesting patterns.

Hello World Edit Form (1)

In this case we are using an existing action and just extend it. The existing "Apply" action saves the data, but also remains in the edit view. Our new action, "Apply and View", saves the data, but forwards the user to the view page.

Hello World Edit Form (2)

The Future -- z3c.form 2.0

Extensions to z3c.form