I'm using Plone with dexterity and I'm validating 2 related fields using the invariant decorator. Everything works but... I'd like to move the generic error message on one specific field.
How can I do this? I've found a three years old suggestion by Martin Aspeli about how it would be cool doing it:
http://plone.293351.n2.nabble.com/plone-app-form-does-not-display-invariant-errors-td348710.html
but they dont came out with a solution.
I've also found a way to do it but it's ugly: placing on the update method of the form this code:
for widget in widgets:
name = widget.context.getName()
if errors:
for error in errors:
if isinstance(error, Invalid) and name in error.args[1:]:
if widget._error is None:
widget._error = error
Isn't there a lower level implementation that allows to pass the names of the fields to the raised Invalid and does not need to loop trough all the fields and all the errors for each field?!?
You can do this by doing your extra validation in the form's action handler, and raise WidgetActionExecutionError specifying the widget for which the error should be shown.
This looks as follows (taken from http://plone.org/products/dexterity/documentation/manual/schema-driven-forms/customising-form-behaviour/validation):
from five import grok
from plone.directives import form
from zope.interface import invariant, Invalid
from zope import schema
from z3c.form import button
from z3c.form.interfaces import ActionExecutionError, WidgetActionExecutionError
from Products.CMFCore.interfaces import ISiteRoot
from Products.statusmessages.interfaces import IStatusMessage
from example.dexterityforms.interfaces import MessageFactory as _
...
class OrderForm(form.SchemaForm):
...
@button.buttonAndHandler(_(u'Order'))
def handleApply(self, action):
data, errors = self.extractData()
# Some additional validation
if 'address1' in data and 'address2' in data:
if len(data['address1']) < 2 and len(data['address2']) < 2:
raise ActionExecutionError(Invalid(_(u"Please provide a valid address")))
elif len(data['address1']) < 2 and len(data['address2']) > 10:
raise WidgetActionExecutionError('address2', Invalid(u"Please put the main part of the address in the first field"))
if errors:
self.status = self.formErrorsMessage
return
I think it may also be possible to raise WidgetActionExecutionError from your invariant, but then it might not do what you want if the invariant is getting checked at some other time than while processing a z3c.form form.