import logging
from pyramid.path import DottedNameResolver
from marshmallow import Schema
From . import ValidationFailure
log = logging.getLogger(__name__)
# request.cstruct validation
def validation_view(view, info):
if info.options.get('validator'):
name_resolver = DottedNameResolver(info.package)
validator = name_resolver.maybe_resolve(info.options.get('validator'))
# If the validator is a subclass of Schema, we assume we need to create
# it and that it has a load function that returns a tuple of
# data/errors
if issubclass(validator, Schema):
def wrapped_validator(jstruct):
(data, errors) = validator().load(jstruct)
# The validator should probably raise by itself...
if errors:
log.debug(errors)
raise ValidationFailure(errors)
return data
validator_func = wrapped_validator
else:
validator_func = validator
def wrapper_view(context, request):
try:
jstruct = request.json_body
except ValueError as e:
log.debug(e)
raise HTTPBadRequest({'error': 'Expecting JSON. Didn\'t work out.'})
if jstruct:
request.appstruct = validator_func(jstruct)
return view(context, request)
return wrapper_view
return view
validation_view.options = ('validator',)
@view_config(
name='forgot',
request_method='POST',
validator='myproject.validation.user.Email',
)
def forgot(self):
request = self.request
dbsession = request.dbsession
appstruct = request.appstruct
user = dbsession.query(m.User)\
.filter(m.User.email == appstruct['email'])\
.one_or_none()
if user is not None:
request.notify(e.UserForgotPassword(request, user))
return HTTPNoContent()
In my case I also allow you to pass a function that validates the input instead of a Marshmallow schema, but the schema works really well for most of my API endpoints. You could off course replace it with Colander or any other validator.
Currently the view deriver just returns the normal view if there is no validator specified, but you could also raise a ConfigError or something similar, you may also require multiple options so you can for example turn off validation for a particular view.
Do note that ValidationFailure takes the marshmallow errors/or your validation function's output and I have a special view that handles ValidationFailures by turning them into HTTPBadRequest's with extra information on what part of the validation failed/why.
Anyway, you get the basic gist of it and hopefully this helps you find a solution to your problem.