How to make forms for Plone 3 the easy way

How to use z3c.form in Plone to create easy and flexible forms. Forget about complicated ways of making forms!

plone.app.z3cform is a Zope package that allows you to easily use z3c.form in your Plone add-on.

Why you should consider using z3c.form

  • Because Archetypes forms are hard to use independently of content types. This has led to several hacks in the past, where Archetypes content types were misused as tools, survey forms etc.
  • Because zope.formlib is often too inflexible and hacky, due to its lack of adaptability and lack of a solid set of widgets.

I've been using z3c.form within Plone extensively for the Singing & Dancing newsletter add-on, and it's been like a breath of fresh air! No hackery, no getting in the way; it's a powerful set of tools for doing what you want to do: make forms.

What is plone.app.z3cform?

plone.app.z3cform is a package that provides widgets and other utilities for making forms in Plone. It also, contains templates for Plone for its big brother, the plone.z3cform package. plone.z3cform in turn is a very thin layer around z3c.form. Forms that you make with plone.app.z3cform are essentially the same as pure Zope 3 forms built with z3c.form. No strings attached, and no special Zope 2/Plone hacks required (like self.context.aq_inner). z3c.form comes with excellent documentation, found in both the z3c.form source tree and online.

How do I use it?

plone.app.z3cform requires Plone 3.0 or higher, and the use of buildout.

Depending on z3c.form

plone.app.z3cform is available PyPI. If you already know how to depend on another package in your application, you can read ahead. A dependency is simply declared by adding the following to the install_requires section of your package's setup.py:

# ...
setup(
# ...
install_requires=[
'plone.app.z3cform',
'my.other.package',
],
# ...
)

In your application package's configure.zcml file you have to declare a dependency on the plone.app.z3cform configuration, like this:

<configure xmlns="http://namespaces.zope.org/zope">
<include package="plone.app.z3cform" />
...
</configure>

Using version pins in buildout

To use plone.z3cform, you will need to add a few version pins to your buildout, since plone.z3cform depends on newer versions of some core components. Something like this should work:

[buildout]
...
versions = versions

[versions]
z3c.form = 1.9.0
zope.i18n = 3.4.0
zope.testing = 3.4.0
zope.component = 3.4.0
zope.securitypolicy = 3.4.0
zope.app.zcmlfiles = 3.4.3

Your mileage may vary. The Singing & Dancing development buildout is an example of a buildout that uses plone.z3cform, through a dedicated "extends file".

Examples

Tons of examples of using z3c.form can be found online. This is an example of a simple form for Plone:

>>> from zope import interface, schema
>>> from z3c.form import form, field, button
>>> from plone.app.z3cform.layout import wrap_form

>>> class MySchema(interface.Interface):
... age = schema.Int(title=u"Age")

>>> class MyForm(form.Form):
... fields = field.Fields(MySchema)
... ignoreContext = True # don't use context to get widget data
... label = u"Please enter your age"
...
... @button.buttonAndHandler(u'Apply')
... def handleApply(self, action):
... data, errors = self.extractData()
... print data['age'] # ... or do stuff

>>> MyView = wrap_form(MyForm)

By using the wrap_form method, we make sure that our form is wrapped in a standard Plone layout, and is placed in the content area.  This means that you can use your form class independently of layout in any part of your Plone site, e.g. in portlets or viewlets. It also means that you don't need your form classes to inherit from Acquisition or Five.

The MyView class is now ready for registration via your standard browser:page directive. For more details on the wrap_form function and how the layout works, see the plone.z3cform.layout module.

For more examples of forms within Plone, please refer to the project's homepage, Kamon Ayeva's two blog posts: 1 and 2, and see the Singing & Dancing packages.

If you have questions, don't hesitate to contact Plone's Add-on Product Developers list, or the authors of Singing & Dancing.

PFG, PloneSurvey, etc

Posted by Yves Moisan at May 09, 2008 03:01 PM
"z3c.form is the next generation Zope forms library"

I've been using PloneFormGen and PloneSurvey for various use cases and I was wondering if there will be or if there should be, in your opinion, a convergence for general purpose forms generation in Plone to z3c.form.

Cheers,

Yves

Re: PFG, PloneSurvey, etc

Posted by Daniel Nouri at May 09, 2008 04:58 PM
PloneFormGen is an excellent Product, but it has a different purpose compared to z3c.form; it's mostly user-oriented in that it allows the creation of forms and action handlers through the web. While z3c.form is a powerful tool aimed at developers. Of course, z3c.form could be *used* to develop something like PFG, which makes a lot of sense. But first of all, it's more low-level, and I hope that it will gradually replace all forms in Plone itself, like the ones based on Python Scripts, CMFFormController, AT, and formlib.

I hope this answers your question. I know nothing about PloneSurvey.

Re: Re: PFG ...

Posted by Yves Moisan at May 09, 2008 05:47 PM
"z3c.form could be *used* to develop something like PFG" is what I suspected. I too very much like PFG. My guess is that PloneSurvey could also use z3c.form machinery, but then I'm a PloneSurvey user, not a developer.

Thanx.

subforms

Posted by Dylan Jay at May 18, 2008 04:05 PM
It would be good to support forms which aren't whole pages or even subforms, yet still with the plone styling.

Re: subforms

Posted by Daniel Nouri at May 18, 2008 06:25 PM
Dylan, this is already possible. The form in this how-to can be embedded anywhere by itself -- just put the HTML returned by MyForm(context, request)() wherever you want.

Singing & Dancing has an example of using z3c.form's subforms with a Plone 3 style without hassle: http://dev.plone.org/[…]/collector.py

overriding templates

Posted by Dylan Jay at May 19, 2008 01:03 AM
The fact that forms are always meant to provide their own template which then uses old fashioned macros such as subform.pt is what I was missing.

1 <div tal:attributes="class view/css_class|nothing">
2 <h3 tal:replace="structure view/heading|nothing" />
3 <div tal:replace="structure view/contents_top|nothing" />
4 <metal:use use-macro="context/@@ploneform-macros/fields" />
5 <metal:use use-macro="context/@@ploneform-macros/actions" />
6 <div tal:replace="structure view/contents_bottom|nothing" />
7 </div>

Re: overriding templates

Posted by Daniel Nouri at May 19, 2008 07:40 AM
Dylan, I changed this so that forms will always fall back to the default Plone style template if not specified differently. I made the change here: http://dev.plone.org/plone/changeset/20906

in case of "zope.component 0.0 has no such extra feature 'zcml'"

Posted by danielle d'avout at Jun 24, 2008 06:03 AM
notice the change of the section fakezope2eggs(june 2008)

Updated to work with 0.3 plone.z3cform and plone.app.z3cform

Posted by Daniel Nouri at Jul 23, 2008 12:43 AM
I changed the import from plone.z3cform.base to plone.app.z3cform.layout. This is where the FormWrapper class and the new wrap_form function have moved to.

Here's the changeset in dancing that migrates the code from using plone.z3cform 0.2 to 0.3: http://dev.plone.org/collective/changeset/68822

To migrate your code to plone.z3cform 0.3 you must change a few imports from plone.z3cform to plone.app.z3cform. Most notably:

    plone.z3cform.base -> plone.app.z3cform.layout
    plone.z3cform.wysiwyg -> plone.app.z3cform.wysiwyg
    plone.z3cform.queryselect -> plone.app.z3cform.queryselect

Also, in your configure.zcml, you must include plone.app.z3cform's configure.zcml instead of plone.z3cform's.

Why is all this necessary? We decided we'd split up the package into one that's completely Plone- agnostic (plone.z3cform) and one that carries Plone- specific widgets and templates (plone.app.z3cform).

Unit testing + translations

Posted by Mikko Ohtamaa at Feb 04, 2009 02:33 AM
I had this exceptions when trying to use z3c.form from unit tests:

  File "/home/moo/sits/parts/zope2/lib/python/zope/tal/talinterpreter.py", line 631, in do_insertText_tal
    text = self.translate(text)
  File "/home/moo/sits/parts/zope2/lib/python/zope/tal/talinterpreter.py", line 841, in translate
    i18ndict, default=default)
  File "/home/moo/sits/parts/zope2/lib/python/zope/app/pagetemplate/engine.py", line 109, in translate
    context=self.request, default=default)
  File "/home/moo/sits/eggs/zope.i18n-3.6.0-py2.4.egg/zope/i18n/__init__.py", line 128, in translate
    return util.translate(msgid, mapping, context, target_language, default)
  File "/home/moo/sits/eggs/zope.i18n-3.6.0-py2.4.egg/zope/i18n/translationdomain.py", line 77, in translate
    target_language = negotiator.getLanguage(langs, context)
  File "/home/moo/sits/parts/plone/PlacelessTranslationService/memoize.py", line 47, in memogetter
    annotations = IAnnotations(request)
TypeError: ('Could not adapt', <z3c.form.testing.TestRequest instance URL=http://127.0.0.1>, <InterfaceClass zope.annotation.interfaces.IAnnotations>)


This helps:

        # Need to hack translations away since they cannot adapt to z3c test request
        # and cause TypeError during form.render()
        from zope.i18n.translationdomain import TranslationDomain
        def dummy_translate(self, msgid, mapping=None, context=None,target_language=None, default=None):
            return msgid

Unit testing + translations

Posted by Mikko Ohtamaa at Feb 04, 2009 10:24 PM
the last copy-paste was missing a line...

        # Need to hack translations away since they cannot adapt to z3c test request
        # and cause TypeError during form.render()
        from zope.i18n.translationdomain import TranslationDomain
        def dummy_translate(self, msgid, mapping=None, context=None,target_language=None, default=None):
            return msgid
        
        TranslationDomain.translate = dummy_translate
                    

ImportError: No module named ImplPython

Posted by Daniel Nouri at Mar 16, 2009 02:41 PM
Note that since the release of zope.component 3.6.0, you need to pin the version of zope.component to 3.5.1. I changed this HOWTO accordingly. If your buildout pulls in zope.component 3.6.0, you'll see:

  ImportError: No module named ImplPython

lists page gone/made private, why?

Posted by Toni Mueller at Apr 16, 2009 08:08 AM
Hello, if I want to access this page: http://plone.org/support/lists

I get "insufficient privileges" even if I am logged in.