z3c.form validator context is not the context content object; rather, it is whatever
Normally, z3c.form stores form data as content object attributes. More specifically, the default
AttributeField data manager directly gets and sets the instance attributes of the context content object.
To register a
SimpleValidator -based validator adapter for the form, the class of that same context object (or an interface provided by it) should be passed to
validator.WidgetValidatorDiscriminators. The same context object is then assigned to the validator’s
context attribute, when it runs.
That’s all fine so far.
Use a custom data manager
Sometimes you may want to for example store form data in annotations, rather than in object attributes. In such a case, you can use a custom
z3c.form data manager that basically just changes what object the form load & save operations are performed on.
All that’s required (for that particular case) is:
- register the built-in (not used by default)
DictionaryFieldas an adapter for
<adapter for="persistent.mapping,PersistentMapping zope.schema.interfaces.IField" provides="z3c.form.interfaces.IDataManager" factory="z3c.form.datamanager.DictionaryField" />
- add to the form a custom
getContentmethod that returns your
Resulting change in validation behavior
It appears that when using a custom data manager, z3c.form enforces its own idea of what the context is considered to be (for purposes of z3c.form validation, anyway).
What does this mean? During form validation by a subclass of
SimpleValidator, contrary to default behavior described in the beginning of this article, the context is no longer the context content object (say, a Page or Folder). Instead, it is what the getContent method returned. In our case, that would be
zope.interface.common.mapping.IMapping if we passed an interface rather than class).
So if you were still expecting the context to refer to the content object, you’d be in for a surprise: First, when passing a
context discriminator to
validator.WidgetValidatorDiscriminators to validate the form, you’d now have to pass
zope.interface.common.mapping.IMapping) instead of the content object (e.g. Page or Folder). Second, when thus registered validator would run, its context attribute would now also be the said PersistentMapping (or the interface).
Why is this bad
While this change in behavior may be by design, the changed semantics is confusing and at the very least not obvious.
It is not intuitive that what could reasonably be considered z3c.form’s internals (how the actual form storage is determined) would propagate to a change in semantics of validation context in this way. Especially when ‘context’ in the Zope/Plone world is pervasively used to refer to the context content object (acquisition-wrapped, but regardless).
Also, to access the actual content object when custom data manager is used, the context content object is not directly accessible (thankfully, the validator can still reach it via the view, ie.