Some of the information here may be outdated, please check the book instead
[edit]

Overview

All web frameworks need to generate HTML pages so they all include a template language to describe the content of these pages. The template language describes how to render data (in the form of XML or a Python dictionary for example) into HTML.

All template languages consist of HTML text embedding code delimited by special tags. The code instructs the renderer how to handle the text. Some tags read like "replace me with this variable", some read like "embed the following text if a certain condition is true", and other tags read like "loop and embed the following text at every iteration".

This is fine and useful but sometimes it gets ugly. In particular it gets ugly when one need to generate HTML code recursively.

Web2py, while providing a python based template language, also provides one more tool: a Server Side Document Object Model (SSDOM).

In other words web2py includes classes that correspond to the HTML tags with the same name (including a catch is all TAG for XML). We call these classes helpers. Here is an example:

>>> a = DIV('hello')
>>> print a
<div>hello</div>

Details

A helper can have attributes (corresponding to the tag attributes) and components (corresponding to the innerHTML).

>>> a = DIV(A('click',_href='http://web2py.com'),B('me'))
>>> print a
<div><a href="http://web2py.com">click</a><b>me</b></div>

Helper objects can be manipulated server side and are serialized when printed.

>>> a = DIV('One','Two')
>>> a.append(DIV('Three'))
>>> print a
<div>OneTwo<div>Three</div></div>

>>> a[2].append('Four')
>>> print a

<div>OneTwo<div>ThreeFour</div></div>
>>> del a[1]
>>> print a
<div>One<div>ThreeFour</div></div>

>>> a[1]['_id']='34'
>>> print a
<div>One<div id="34">ThreeFour</div></div>

>>> a.element(_id='34')['_class']='x'
>>> print a
<div>One<div class="x" id="34">ThreeFour</div></div>

The helper objects serialize themselves recursively and safely. Safely means that all atomic strings contained in each object are properly html-escaped. All strings contained in the attributes are properly url-encoded.

>>> a = div('<click>',_onclick='a="b"')
>>> print a
<div onclick="a=\"b\"">&lt;click&gt;</div>

This prevents XSS vulnerabilities and removed the burden from the developer to remember when to escape and when to encode.

Helper attributes must have names that start with underscore to avoid name conflicts ("class" for example can be an attribute but it is also a Python keyword).

All more complex web2py objects such as FORM and SQLFORM (that turns database models into create/update forms) are helpers.

The web2py template language is helper-aware and know how to serialize a helper when its embedded into a piece of html:

<html><body>{{=a}}</body></html>

While this functionality may seem trivial it gets very handy for manipulating html server size without using strings. Moreover they generate HTML/XML that is 100% valid.

Let's consider the problem of a rendering a tree structure represented by nested python lists as nested HTML lists. Here is how to do with helpers:

>>> a=['One', 'Two', ['Three', 'Four', ['Five', 'Six']]]
>>> def build(item):
>>>     if isinstance(item,list):
>>>         return UL(*[LI(build(x)) for x in item])
>>>     else:
>>>         return item
>>>
>>> print build(a)
<ul><li>One</li><li>Two</li><li><ul><li>Three</li><li>Four</li><li><ul><li>Five</li><li>Six</li></ul></li></ul></li></ul>

(web2py also provides a way to define functions in templates and below we show an example of how to do define this very same function using that different technology, although that is also a web2py specific mechanism and it is not the main topic of discussion here).

Some web2py helpers are smarter than others in the sense that they know what type of elements they should contain and can convert them as necessary. For example a UL should contain LI items hence the following line in the previous example

>>>         return UL(*[LI(x) for x in item])

can safely be replaced by

>>>         return UL(*[build(x) for x in item])

(notice that *item breaks the item list into its elements).

This mechanism of helpers can also be used to generate any XML via a generic TAG helper:

>>> a = TAG.items(TAG.item(1),TAG.item(2),_name='parent')
>>> print a
<items name="parent"><item>1</item><item>2</item><items>

Here item and items are not keywords. There are been defined here. TAG.name(...) is the same as TAG['name'](...) and it corresponds to <name>...</name>.

web2py also provides other special helpers.

The CODE helper for example does syntax highlighting of its content and uses a special attribute language (without underscore) to specify the language (value can be "python", "html", "web2py", or "cpp").

>>> a = CODE('for i in range(10): print i', language='python')

The FORM helper has a special method called accept that knows how to process an HTTP request containing a form generated but itself and self submitted (postback). By processing we mean that it looks in the request for variables defined by its inner INPUT and TEXTAREA components, validates them, and modify itself to report possible validation errors.

The SQLFORM helpers is a FORM that is automatically generated from a model (i.e. a database table defined in web2py). So if db.mytable is a table

>>> form = SQLFORM(db.mytable)

is a create form, and

>>> form.accepts(request.vars)

does all the required processing and returns True if the request.vars are accepted by the form. If validation passes the variables are stored in a new record, else errors are reported to the visitor. Here is a complete web2py action for a "create db.mytable" form:

>>> def myaction():
>>>    form = SQLFORM(db.mytable)
>>>    if form.accepts(request.vars):
>>>          response.flash='record inserted'
>>>    elif form.errors:
>>>          response.flash='there are errors, they are shown in form'
>>>    return dict(form=form)

The web2py helpers are simple and yet powerful. Their names correspond the HTML tags that they represent and therefore are easy to remember. You can use most of them without learning any new keyword.

Functions in templates

Web2py also provides an alternative way to build recursive structures using exclusively the template language. In fact it is possible to define functions in the template language that mix code and html. Here is an example:

{{def build(item):}}
  {{if isinstance(item.list):}}
    <ul>{{for element in item:}}<li>{{build(element)}}</li>{{pass}}</ul>
  {{else:}}
    {{=item}}
  {{pass}}
{{return}}
© 2008-2010 by Massimo Di Pierro - All rights reserved - Powered by web2py - design derived from a theme by the earlybird
The content of this book is released under the Artistic License 2.0 - Modified content cannot be reproduced.