I have a cornice API with a view that has validators and a colander schema. I can't get access to colander validated data (request.validated
) in my validator.
I pass my data through colander. My colander schema looks something like this:
from colander import (
MappingSchema,
SchemaNode,
String
)
class UserSchemaRecord(MappingSchema):
username = SchemaNode(String())
password = SchemaNode(String())
class UserSchema(MappingSchema):
user = UserSchemaRecord()
It adds a sanitized version of the request data into request.validated['user']
that I can then access in my view like this.
@view(renderer='json', validators=(valid_token, valid_new_username), schema=UserSchema)
def collection_post(self):
"""Adds a new user"""
user_data = self.request.validated['user']
user = UserModel(**user_data)
DBSession.add(user)
DBSession.flush()
return {'user': user}
However, I also need to check that the request provides a unique username and return an error if the username is already taken. I'd like to do this with a validator (valid_new_username
) but when I try to access request.validated['user']
in my validator the data aren't there.
def valid_new_username(request):
user = request.validated['user'] # this line fails with a KeyError
username = user['username']
if UserModel.get_by_username(username):
request.errors.add('body', 'username', "User '%s' already exists!" % username)
request.errors.status = 409 # conflict
It looks like the validator is called before the data have been extracted. I don't really want to access the request json_body data directly before passing them through colander. Is there a way I can change the ordering of the schema/validator?
The alternative is to do the checking directly in my view callable. Is that a good option? Are validators not supposed to work with colander validated data?
Not sure if it's your problem, but if the data is not valid in the first place (Colander found errors), then it will not be available as request.validated['key'] in the validators.
You can use a decorator like this if you want to apply the validator only if the data has passed Colander validation.
def when_valid(validator):
"""Decorator for validation functions.
Only try validation if no errors already.
"""
def inner(request):
if len(request.errors) > 0:
return
validator(request)
return inner
@when_valid
def valid_new_username(request):
pass # your validation here