We used the same approach with the widget-based validator introduced by @petschki; however, we went a step further by displaying multiple errors while validating the DataGridField. This might be useful for others.
First, you need to register a widget-level validator for your DataGridField (DGF).
from z3c.form import validator
validator.WidgetValidatorDiscriminators(
MyDGFValidator,
field=IMySchema["presentations"],
)
component.provideAdapter(MyDGFValidator)
register the validator in ZCML
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="">
<adapter factory=".MyDGFValidator" name="presentations" />
</configure>
Implement a validation object. To me it seems important to apply rules to each row of the DataGridField (DGF). I particularly like the monadic approach described in this article: Object-Oriented vs. Functional Form Validation.
class MyDGFValidator(validator.SimpleFieldValidator):
def validate(self, value):
try:
# Some validation call which applies validation rules to each row or whole table
# and returs errors
# I prefer more functional approach
result = ValidatedData(value).run(*validators)
except Exception as err:
raise Invalid("Validation chain internal error: {}".format(err))
errors = list(result['errors'].values())
if errors:
raise MultiInvalid([Invalid(e) for e in errors])
If you're using Zope version lower than 5.0, the MultiInvalid exception is not available. In this case, you need
-
Raise a z3c.form.error.MultipleErrors
object from the validate method instead of MultiInvalid
. This MultipleErrors
object will be passed to your custom ErrorViewSnippet.
-
Register a custom ErrorViewDiscriminator for your field and the exception raised (in this case, MultipleErrors
). The discriminator defines and calls your custom ErrorView to handle multiple errors.
from z3c.form import error
error.ErrorViewDiscriminators(
MyDGFFieldValidationErrorView,
error=error.MultipleErrors,
field=IMySchema["presentations"],
)
component.provideAdapter(InterimsFieldValidationErrorView)
class SingleRowErrorViewSnippet(ErrorViewSnippet):
def __init__(self, error, request, widget, field, form, content):
super(SingleRowErrorViewSnippet, self).__init__(
error, request, widget, field, form, content)
self.message = self.content
def update(self):
pass
class MyDGFFieldValidationErrorView(MultipleErrorViewSnippet):
def __init__(self, error, request, widget, field, form, content):
super(MyDGFFieldValidationErrorView, self).__init__(
error, request, widget, field, form, content)
err_snippet = partial(SingleRowErrorViewSnippet,
error, request, widget, field, form)
self.error.errors = (err_snippet(err) for err in self.error.errors)