0

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.

Matthew Trevor
  • 14,354
  • 6
  • 37
  • 50

1 Answers1

1

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.
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I've tried this approach. Calling `createContentInContainer` _before_ validating the data will raise exceptions if any of the constraints are violated, which I'd rather avoid having to handle separately. None of the validator classes fire, probably because they're registered directly to widgets on the form. Invariants do work, but they lack the context I need for the rest of the validation. I'm currently using `getValidationErrors` on a mock of the item before creation, and then manually running the validator classes on specified fields, but it seems kludgy. – Matthew Trevor Aug 23 '12 at 11:04