When using TurboGears, your controller methods get their arguments built from
the various GET, POST, and URL mechanisms provided by CherryPy. The only
downside is that all the arguments will be strings and you’d like them
converted to their normal Python datatype: numbers to
int, dates to
This conversion functionality is provided by the FormEncode package and is
applied to your methods using the
@validate() decorator. FormEncode
provides both validation and conversion as a single step, reasoning that you
frequently need to validate something before you can convert it or that you’ll
need to convert something before you can really validate it.
@validate() decorator can evaluate both widget-based forms and the
standard CherryPy arguments. The
@validate() decorator is not required in
order to use TurboGears. If you don’t put a
@validate() decorator on your
method, you’ll simply have to do the string validation and conversion yourself.
The simplest way to use
@validate() is to pass in a reference to a
widgets-based form instance:
@expose('yourpkg.templates.aform') @validate(yourpkg.forms.my_form) def show_form(self, field1, field2, ...): ... return dict(form=yourpkg.forms.a_form)
yourpkg.forms.a_form is an instance of a
sub-class or a callable that returns such an instance. The callable will be
called for each request, so you can dynamically build forms depending on the
The widgets system will take care of building a schema to handle the data
conversions and you’ll wind up with the
datetime objects you
specified when building the form. When paired with the error handling
decorators, you can handle the common case of building a form, validating it,
re-displaying the form if there are errors, and converting a valid form into the
proper arguments in only a few lines of Python.
When not using forms, the story gets a bit more complex. Basically, you need to
specify which validator goes with which argument using the
keyword argument for the
validate decorator. Here’s a simple example:
from turbogears import controllers, expose, validate, validators class Root(controllers.RootController): @expose('yourpkg.templates.someform') @validate(validators = dict( # Only letters/numbers/underscores/dashes item=validators.PlainText(), # Default value 'M' size=validators.OneOf(['S', 'M', 'L'], if_empty='M'), # Required field price=validators.Money(not_empty=True))) def storeitem(self, tg_errors=None, **kwargs): if tg_errors: pass # handle the error however you want! return dict(newvalue=value*2)
As you can see FormEncode and TurboGears provide a number of useful validators
that are available in the
For most validators, you can pass keyword arguments for more specific constraints. For the list of available validators, check both the FormEncode validators and TurboGears validators (source) modules. You can also create your own validators or build on existing validators by inheriting from one of the defaults. See the FormEncode documentation for how this is done.
The dictionary passed to the
validators argument of the
decorator maps the incoming field names to the appropriate FormEncode
validators - in this example, we used
If all values pass validation,
tg_errors won’t change from it’s default
None and you are assured that value will be an
Wait a minute! What’s this
If there’s a validation error, a dictionary named
tg_errors will be passed
to the function, with keys for the field names that failed validation. If you
look up the keys, such as
tg_errors['value'], you’ll find an
Invalid exception objects can be
str()‘d if you’d like
to use them as user-friendly error messages or you can use the information
contained in them to handle the error as you like:
@expose('yourpkg.templates.someform') @validate(validators=dict(value=validators.Int())) def index(self, value=0, tg_errors=None): if tg_errors: message = ', '.join(str(item) for item in tg_errors.values()) else: message = "Success!" return dict(message=message)
If you use form-based validation or if you pass a validation schema to the
validators argument (see below), the values in the
already be coerced into strings for your convenience. If you still need
access to the
Invalid exception object thrown by the validation schema,
you can access it through
Invalid exceptions for the individual fields that failed
validation through its
If you omit the
tg_errors=None keyword from the controller method’s
arguments, you get told that there is no error handler function. This is
a bit misleading as you don’t really need an error handler if you handle
things on your own, as explained here. See the documentation on
Error Handling for learning more about error handlers.
If you want to do multiple-field validation, reuse validators or just clean up
your code, validation
Schema``s are the way to go. You create a validation
schema by inheriting from ``turbogears.validators.Schema and pass the newly
Schema as the
validators argument instead of passing a
class MySchema(validators.Schema): pwd1 = validators.String(not_empty=True) pwd2 = validators.String(not_empty=True) chained_validators = [validators.FieldsMatch('pwd1', 'pwd2')] @expose() @validate(validators=MySchema()) def password(self, pwd1, pwd2, tg_errors=None): if tg_errors: return "There was an error" return "Password ok!"
Besides noticing our brilliant security strategy, please notice the
chained_validators part of the schema that guarantees a pair of matching
Again, for information about
Invalid exception objects, creating your own
validators, schema and FormEncode in general, refer to the FormEncode
Validator documentation and don’t be afraid to check the
formencode.validators source. It’s often more clear than the docs.
You can always use e.g. Python’s
int() method to convert a string to an
integer and use a try/except block to catch errors in the conversion process:
from turbogears import controllers, expose class Root(controllers.RootController): @expose() def addnum(self, x, y): """Return the result of x+y.""" try: return str(int(x) + int(y)) except ValueError: return "Input is not valid!"
This isn’t that hard, but it quickly becomes unwieldy when you start converting large numbers of arguments. Moreover, you still have the problem of propagating the errors back to your users. In the end, it’s usually far simpler to use the validation framework.
Sometimes you need access to external and possibly dynamic information in your
validators, for example to change the outcome of the validation depending on this
_to_python methods of all validators
take an optional argument called
state which is exactly for this purpose. The
value of this argument should be a
turbogears.util.Bunch-like object (i.e. just
a simple data class that has arbitrary attributes) which provides the state
information for the validator (schema). See the FormEncode documentation about
‘state’ for more information.
validate decorator function takes a keyword argument named
which expects a callable, which should return a Bunch object suitable to be passed
as the ‘state’ parameter to the validator schema of the form widget.
Here’s a small example of how you can provide a validator with access to information about the current request:
from turbogears import util, expose, validate from myproject.forms import myform def state_provider(): return util.Bunch(request=cherrypy.request) @expose() @validate(form=myform, state_factory=state_provider) def mymethod(self, ...) ...
When the arguments, for whatever reason, fail validation, you will receive no values for the failing arguments.
If you would rather not get a
tg_errors dict when this occurs, simply
provide the validator with a value for its
if_invalid keyword argument.
If you’d like to get both a
tg_errors dict (so you can do some error
handling) and a default value, supply a dictionary to the
keyword argument containing the parameter/value pairs:
@expose() @validate(validators=MySchema(), failsafe_values=dict(pwd1='swordfish')) def password(self, pwd1, pwd2, tg_errors=None): if tg_errors: return "There was an error" return "Password ok!"