Validation done manually (aka "die, @validate, die")
For my taste the @validate decorator is not flexible enough. So I found myself doing the form validation with FormEncode a little more verbose and manually.
This is the validate function that does the actual validation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | def validate(schema, **state_kwargs):
"""Validate a formencode schema.
Works similar to the @validate decorator. On success return a dictionary
of parameters from request.params. On failure throws a formencode.Invalid
exception."""
# Create a state object if requested
if state_kwargs:
state = State(**state_kwargs)
else:
state = None
# In case of validation errors an exception is thrown. This needs to
# be caught elsewhere.
return schema.to_python(pylons.request.params, state)
|
The state is nothing fancy. Just a simple Python object with some attributes.
A state can be passed to the validator to transport information like who is
currently logged in. The state is completely optional here. My state object is just:
1
2
3
4
5
6
7
8
9
10
11
12 | class State(object):
"""Trivial class to be used as State objects to transport information to formencode validators"""
def __init__(self, **kw):
for key in kw:
setattr(self, key, kw[key])
def __repr__(self):
atts = []
for key in self.__dict__:
atts.append( (key, getattr(self, key)) )
return self.__class__.__name__ + '(' + ', '.join(x[0] + '=' + repr(x[1]) for x in atts) + ')'
|
In case the validation failed you want to redisplay the current HTML page
and decorate the input fields with the appropriate error messages just like
the @validate decorator does. This is the function I use to prepare such
a page with error messages:
1
2
3
4
5
6
7
8
9
10 | def htmlfill(html, exception_error=None):
"""Add formencode error messages to an HTML string.
'html' contains the HTML page with the form (e.g. created with render()).
'exception_error' is the formencode.Invalid-Exception from formencode."""
return formencode.htmlfill.render(
form=html,
defaults=pylons.request.params,
errors=(exception_error and exception_error.unpack_errors()),
encoding=pylons.response.determine_charset()
)
|
Now this is how you would use these functions in your controller:
1
2
3
4
5
6 | try:
fields = validate(MyValidationSchema)
except formencode.Invalid, e:
return htmlfill(self.index(), e)
# 'fields' now contains the form information.
|
I'm using this code in production and it does what I expect. Let me know if you have improvements or ideas.
Christoph