Approximately in the spring of 2012, we left Archetypes-based content types in favour of Dexterity-based content types. To us Dexterity was mature enough for production environments and just some of the many advantages that made us change did include:
- More light-weight objects that will easier scale on bigger sites.
- The advantage of z3c.form and all the extensions provided.
- Elegant extension of already existing types by Dexterity behaviors (compared to Archetypes Schema Extender).
We found the existing work done for Dexterity/z3c.form of a high quality and good design. For example, with plone.directives.form, it was easy to change widgets, set default values of fields etc.; but it did not take long before we got stuck doing just simple things. Just to mention one example, you have a schema defined by a regular z3c.form interface, you desire to change the widget out side the class level. Here plone.directives.form cannot help you, instead you need to set the tagged value manually. It is not to criticize plone.directives.form in any way, but at least we saw it was problematic to use for general purposes. We found tagged values to be a sufficient solution in this case, i.e. plone.directives.form is just setting a tagged value on the particular field when changing widget.
Further troubles were ahead. Dexterity behaviours are very powerful, and it gets hard to get back to old Archetypes-based sites, and do something elegant, as you can do with behaviours. But consider you have a behaviour there is shared over many content types. You want just for one content type a field in the behaviour to be required. If you are unaware of IFormExtender here you are really lost. I found this interface by discussing missing adapter lookups with David Glick (unfortunately I lost the github issue reference).
When you have a factory adapting Interface, IDefaultBrowserLayer, IForm
and implementing IFormExtender, you can ensure the update method is run every time a form is rendered. You could of course implement custom forms for your specific dexterity content types but this would be very time consuming. Instead of adapting on Interface you can also adapt on the specific behavior/schema interface.
Example
We have a content type, lets call it publication, this content type has the behavior IDublinCore (providing Dublin Core metadata for Dexterity content types). We desire the effective date field (effective) is required just for this content type. We create the following factory (lets store it in factory.py).
# -*- coding: utf-8 -*- from zope.publisher.interfaces.browser import IDefaultBrowserLayer from zope.interface import Interface, implements from zope.component import adapts from plone.z3cform.fieldsets.extensible import FormExtender from plone.z3cform.fieldsets.interfaces import IFormExtender from z3c.form.field import Fields from z3c.form.interfaces import IEditForm, IAddForm, IForm class EffectiveDateRequired(FormExtender): implements(IFormExtender) adapts(Interface, IDefaultBrowserLayer, IForm) def __init__(self, context, request, form): self.context = context self.request = request self.form = form def update(self): is_form = IEditForm.providedBy(self.form) or \ IAddForm.providedBy(self.form) if hasattr(self.form, 'portal_type') and \ self.form.portal_type == 'Publication' and \ is_form: field = effective_group.fields['IDublinCore.effective'] field.field.required = True else: field = effective_group.fields['IDublinCore.effective'] field.field.required = False
The update method will be called everytime a form is rendered so it is important for us to check if it is either an edit- or add form. Further on we check that we only handle the Publication type.
The adapter needs to be registered, which we do trivially via configure.zcml
<configure xmlns="http://namespaces.zope.org/zope"> <adapter factory=".factory.EffectiveDateRequired" /> </configure>
Concluding remarks
IFormExtender is in my opinion the universal fix where you get stuck with plone.directives.form or any other “convenience library”. It is nice to have such a fix but I would rather see plone.directives.form improved, to support more general use-cases.