Section 17.5. Decorators


17.5. Decorators

As you have already seen in the first few sections of the book, TurboGears makes extensive use of Python decorators to minimize boilerplate and promote the DRY (Don't Repeat Yourself) principle. And we've already covered the expose() and validate() decorators. But don't let the simplicity of their application programming interface (API) fool you. There's a lot going on under the hood, and it will be well worth our time to delve into some of their secrets because they can definitely help you create better and more flexible controller methods.

17.5.1. expose()

The TurboGears expose() decorator is an extension to CherryPy's expose decorator. It controls not only what methods should be accessible from the outside world but also how the output will be formatted. TurboGears uses the generic function mechanism from Philip Eby's RuleDispatch package. And much of the power and flexibility of the TurboGears implementation of the expose decorator is a result of the inherent flexibility of generic functions.

One of TurboGear's fundamental goals was to allow us to have a number of different "views" of the same controller method, just by adding additional expose decorators like this:

@expose("cheetah:mypackage.templates.text", as_format="text") @expose("mypackage.templates.xml", accept_format="application/xml") @expose("json") @expose("mypackage.templates.view") def view(self):    pass


The first argument denotes how the output will be rendered, while as_format and accept_format help determine which rendering method will be used. Let's examine this declaration step by step:

@expose("cheetah:mypackage.templates.text", as_format="text")


This decorator tells TurboGears to use the Cheetah template engine ("cheetah:") to render a template named text located in mypackage.templates (a Python path is used when resolving) if the dictionary returned by the controller contains the key value pair ("tg_format":"text").

@expose("mypackage.templates.xml", accept_format="application/xml")


This second example uses accept_format, which checks the HTTP Headers to see if the browser (or other user agent) specified a preferred format. In this case it's checking to see if the requested format is "application/xml".

@expose("json")


After the default Kid template rendering, this is the second most common way that TurboGears controller methods are exposed to the web. So we've added a shortcut, so that a simple expose ('json') call will return results from the controller method serialised via JSON, if mapping "tg_format":"json" is returned by replacement in the controller method.

@expose("mypackage.templates.view")


If need be, the template to use can be set from within the controller method by returning a dictionary containing the key tg_template:

@expose() def view(self):        return {"tg_template":"mypackage.templates.view"}


expose() also accepts the following (keyword) arguments:

content_typecontent-type HTTP header.

fragmentis template only a fragment to be included into another page?

17.5.2. validate()

The validate() decorator validates input, and if it is valid, converts it into corresponding Python objects. These two steps can be performed on both the individual arguments to your controller methods and complete forms (see Chapter 16, "TurboGears Widgets," for more detail).

For individual arguments, you can easily set up validators for individual keyword arguments by setting up a dictionary where the keys are the names of the arguments, and the values are the validators you would like used on each argument. Here's a sample:

@validate(validators={"num":validators.Int(),          "content":validators.NotEmpty()}) def page(self, num, content=None):


Note that validation is not limited to keyword arguments.

Validating forms is even simpler:

@validate(form=my_form) def register(self, name=None, age=None, email=None):


Sometimes you need to get access to some "state" information outside of a request in order to validate your data. Fortunately enough, validators can have access to additional information to help them do their validation and that you can pass it along by providing a callable as state_factory.

Here's a brief example:

def state(state):     while True:         yield state         state += 1 state = state(42).next @validate(form=my_form, state_factory=state)


If validation fails, control is passed to the error-handling mechanism, which brings us to TurboGears error- and exception-handling mechanisms.




Rapid Web Applications with TurboGears(c) Using Python to Create Ajax-Powered Sites
Rapid Web Applications with TurboGears: Using Python to Create Ajax-Powered Sites
ISBN: 0132433885
EAN: 2147483647
Year: 2006
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net