Matthew Trevor
Matthew Trevor

Reputation: 14961

Dexterity: validating data before programmatic creation

When creating Dexterity types programmatically, is it possible to validate the data being passed to the factory as if it was coming from a form?

I have a Dexterity content type with a number of constraints, invariants & validators:

from zope import schema
from zope import interface
from z3c.form import validator
from plone import dexterity
from plone.directives import form
from five import grok

def is_not_foo(value):
    return value is not 'foo'

class IMyType(form.Schema):
    my_value = schema.TextLine(
        constraint = is_not_foo
    )
    min = schema.Int(default=0)
    max = schema.Int(default=99)

    @interface.invariant
    def max_more_than_min(data):
        if data.min is not None and data.max is not None:
            if data.min >= data.max:
                raise interface.Invalid( u'Min is more than Max' )

@form.error_message(form=IMyType['my_value'], error=schema.interfaces.ConstraintNotSatisfied)
def error_my_value_is_foo(value):
    return u'my_value must not be "foo"'

class ValidateMyValue(validator.SimpleFieldValidator):
    def validate(self, value):
        if value is not None:
            # value passes some test

validator.WidgetValidatorDiscriminators(
    ValidateMyValue,
    form = IMyType['my_value'],
)
grok.global_adapter(ValidateMyValue)

To ensure that all instances of MyType have the right defaults, I've made a custom class for it with attributes mapped using FieldProperty, and assigned it in the type's FTI declaration:

class MyType(dexterity.content.Item):
    interface.implements(IMyType)

    my_value = schema.fieldproperty.FieldProperty(IMyType['my_value'])
    min = schema.fieldproperty.FieldProperty(IMyType['min'])
    max = schema.fieldproperty.FieldProperty(IMyType['max'])

However, when using plone.dexterity.utils.createContentInContainer, it is possible to instantiate an object that doesn't comply with the constraints etc imposed. Ideally, I'd like to be able to perform this validation myself before doing the creation.

Is there a simple approach here I'm missing? I've taken a brief look at collective.transmogrifier but I see no mention of input validation.

Thanks.

Upvotes: 0

Views: 446

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1124718

You can use the zope.schema.getValidationErrors() function:

 newInstance = createContentInContainer(folder, id, **schemavalues)
 errors = getValidationErrors(IMyType, newInstance)
 if errors:
     # Schema not validated; errors is a sequence of (field, exception) tuples
     # field is None if the error is for an invariant.

Upvotes: 1

Related Questions