I found myself having the same need as Vincent so I looked a little bit
at z3c.form. The end result is very much a mixed blessing. As Vincent
discovered it is not very hard to get z3c.form 2.1 to manage a list of
items. In my example I have this interface which is repeated:
class IInformationLink(Interface):
"""A link to more information related to a risk."""
url = schema.URI(
title = _(u"URL"),
required = True)
description = schema.TextLine(
title = _(u"Description"),
required = False)
this has to be an Interface: if you use a zope.schema.Schema things
break badly. You can then use this in your main interface like this:
class IRisk(form.Schema, IRichDescription, IBasic):
"""A possible risk that can be present in an organisation.
"""
links = schema.List(
title = _(u"Information links"),
description = _(u"A list of links to webpages which provide"
u" more information on this risk."),
required = False,
value_type = schema.Object(
title = _(u"Link"),
schema = IInformationLink))
This works, but the resulting markup from z3c.form is quite ugly: a big
mess of divs, nested divs, label elements inside div.label elements,
etc. The UI it presents is also very confusing:
https://photos-1.getdropbox.com/i/o/JmLD1SNVPJbwy12pgo-FYOWLsG-9L9lKNdBCm6yIfok
is an example. Notice how it is very unclear that the Add/Remove buttons
at the button belong to the 'Links' field. It gets even worse if you try
to insert some data:
https://photos-1.getdropbox.com/i/o/ZrPquRJE5oEOZn1DhCdS6ZTDArNqKhAZ15UGtF-3sqA
. Part of this is a flaw in zope.schema which does not except URLs
without a http: prefix (which will confuse users). The 'The system could
not process the given value' is a mysterious one and reveals an
essential bit that is not documented: z3c.form requires a IObjectFactory
factory for the IInformationLink interface. This is an multi-adapter
(with 4 facets, almost none of which you will ever need) and requires
the name of the adapter to be set to the interface (feels like ZCA abuse
to me):
from zope.interface import Interface
from zope.interface import implements
from zope.component import adapts
from z3c.form.interfaces import IObjectFactory
class InformationLinkFactory(object):
adapts(Interface, Interface, Interface, Interface)
implements(IObjectFactory)
def __init__(self, context, request, form, widget):
pass
def __call__(self, value):
return dict(value)
which you can register in zcml like so:
<adapter factory=".risk.InformationLinkFactory"
name="euphorie.content.risk.IInformationLink" />
and then it still does not work since it requires that your object
factory returns something that zope.schema.Object can handle. In other
words: it is not possible to return dictionaries.
Next attempt: use a simple object that can implement IInformationLink:
class InformationLink(object):
implements(IInformationLink)
def __init__(self, value):
self.url=value["url"]
self.description=value["description"]
class InformationLinkFactory(object):
adapts(Interface, Interface, Interface, Interface)
implements(IObjectFactory)
def __init__(self, context, request, form, widget):
pass
def __call__(self, value):
return InformationLink(value)
and that finally works. The lessons I have learned from this are:
- z3c.form produces very awful HTML
- z3c.form produces very confusing user interfaces
- z3c.form documentation is indeed incomplete (no mention of
ObjectFactory)
- z3c.form you can do cool stuff with it, although I am not convinced
it is much better than hand-coding the form
And the reason I cc'ed the framework team: I strongly feel that until
the generated markup and user interfaces from z3c.form are improved to
meet Plone standards Plone itself should start using it.
Wichert.
_______________________________________________
Framework-Team mailing list
Framework-Team@lists.plone.org
http://lists.plone.org/mailman/listinfo/framework-team