The views

views

template language

HTML

web2py uses Python for its models, controllers, and views, although it uses a slightly modified Python syntax in the views to allow more readable code without imposing any restrictions on proper Python usage.

The purpose of a view is to embed code (Python) in an HTML document. In general, this poses some problems:

How should embedded code be escaped?

Should indenting be based on Python or HTML rules?

web2py uses {{ ... }} to escape Python code embedded in HTML. The advantage of using curly brackets instead of angle brackets is that it's transparent to all common HTML editors. This allows the developer to use those editors to create web2py views.

Since the developer is embedding Python code into HTML, the document should be indented according to HTML rules, and not Python rules. Therefore, we allow unindented Python inside the {{ ... }} tags. Since Python normally uses indentation to delimit blocks of code, we need a different way to delimit them; this is why the web2py template language makes use of the Python keyword pass.

A code block starts with a line ending with a colon and ends with a line beginning with pass. The keyword pass is not necessary when the end of the block is obvious from the context.

Here is an example:

{{
if i == 0:
response.write('i is 0')
else:
response.write('i is not 0')
pass
}}

Note that pass is a Python keyword, not a web2py keyword. Some Python editors, such as Emacs, use the keyword pass to signify the division of blocks and use it to re-indent code automatically.

The web2py template language does exactly the same. When it finds something like:

When there is an error in a web2py view, the error report shows the generated view code, not the actual view as written by the developer. This helps the developer debug the code by highlighting the actual code that is executed (which is something that can be debugged with an HTML editor or the DOM inspector of the browser).

Also note that:

{{=x}}

generates

response.write

escape

response.write(x)

Variables injected into the HTML in this way are escaped by default. The escaping is ignored if x is an XML object, even if escape is set to True.

Here is an example that introduces the H1 helper:

{{=H1(i)}}

which is translated to:

response.write(H1(i))

upon evaluation, the H1 object and its components are recursively serialized, escaped and written to the response body. The tags generated by H1 and inner HTML are not escaped. This mechanism guarantees that all text --- and only text --- displayed on the web page is always escaped, thus preventing XSS vulnerabilities. At the same time, the code is simple and easy to debug.

The method response.write(obj, escape=True) takes two arguments, the object to be written and whether it has to be escaped (set to True by default). If obj has an .xml() method, it is called and the result written to the response body (the escape argument is ignored). Otherwise it uses the object's __str__ method to serialize it and, if the escape argument is True, escapes it. All built-in helper objects (H1 in the example) are objects that know how to serialize themselves via the .xml() method.

This is all done transparently. You never need to (and never should) call the response.write method explicitly.

Basic syntax

The web2py template language supports all Python control structures. Here we provide some examples of each of them. They can be nested according to usual programming practice.

This example illustrates that all output generated before an exception occurs is rendered (including output that preceded the exception) inside the try block. "Hello" is written because it precedes the exception.

def...return

def

return

The web2py template language allows the developer to define and implement functions that can return any Python object or a text/html string. Here we consider two examples:

It produces exactly the same output as above. In this case, the function itemize2 represents a piece of HTML that is going to replace the web2py tag where the function is called. Notice that there is no '=' in front of the call to itemize2, since the function does not return the text, but it writes it directly into the response.

There is one caveat: functions defined inside a view must terminate with a return statement, or the automatic indentation will fail.

HTML helpers

helpers

Consider the following code in a view:

{{=DIV('this', 'is', 'a', 'test', _id='123', _class='myclass')}}

it is rendered as:

<div id="123" class="myclass">thisisatest</div>

DIV is a helper class, i.e., something that can be used to build HTML programmatically. It corresponds to the HTML <div> tag.

Positional arguments are interpreted as objects contained between the open and close tags. Named arguments that start with an underscore are interpreted as HTML tag attributes (without the underscore). Some helpers also have named arguments that do not start with underscore; these arguments are tag-specific.

Instead of a set of unnamed arguments, a helper can also take a single list or tuple as its set of components using the * notation and it can take a single dictionary as its set of attributes using the **, for example:

Note, the complete set of components can be accessed via a list called a.components, and the complete set of attributes can be accessed via a dictionary called a.attributes. So, a[i] is equivalent to a.components[i] when i is an integer, and a[s] is equivalent to a.attributes[s] when s is a string.

Notice that helper attributes are passed as keyword arguments to the helper. In some cases, however, attribute names include special characters that are not allowed in Python identifiers (e.g., hyphens) and therefore cannot be used as keyword argument names. For example:

DIV('text', _data-role='collapsible')

will not work because "_data-role" includes a hyphen, which will produce a Python syntax error.

In such cases, you can instead pass the attributes as a dictionary and make use of Python's ** function arguments notation, which map a dictionary of (key:value) pairs into a set of keyword arguments:

Un-escaped executable input such as this (for example, entered in the body of a comment in a blog) is unsafe, because it can be used to generate Cross Site Scripting (XSS) attacks against other visitors to the page.

sanitize

The web2py XML helper can sanitize our text to prevent injections and escape all tags except those that you explicitly allow. Here is an example:

The XML constructors, by default, consider the content of some tags and some of their attributes safe. You can override the defaults using the optional permitted_tags and allowed_attributes arguments. Here are the default values of the optional arguments of the XML helper.

and a click on the link causes the content to be loaded in the div. This is similar but more powerful than the above syntax since it is designed to refresh page components. We discuss applications of cid in more detail in Chapter 12, in the context of components.

These ajax features require jQuery and "static/js/web2py_ajax.js", which are automatically included by placing {{include 'web2py_ajax.html'}} in the layout head. "views/web2py_ajax.html" defines some variables based on request and includes all necessary js and css files.

CENTER

CODE

CODE

This helper performs syntax highlighting for Python, C, C++, HTML and web2py code, and is preferable to PRE for code listings. CODE also has the ability to create links to the web2py API documentation.

If a link value is specified, for example "/examples/global/vars/", web2py API references in the code are linked to documentation at the link URL. For example "request" would be linked to "/examples/global/vars/request". In the above example, the link URL is handled by the "vars" action in the "global.py" controller that is distributed as part of the web2py "examples" application.

The counter argument is used for line numbering. It can be set to any of three different values. It can be None for no line numbers, a numerical value specifying the start number, or a string. If the counter is set to a string, it is interpreted as a prompt, and there are no line numbers.

The styles argument is a bit tricky. If you look at the generated HTML above, it contains a table with two columns, and each column has its own style declared inline using CSS. The styles attributes allows you to override those two CSS styles. For example:

{{=CODE(...,styles={'CODE':'margin: 0;padding: 5px;border: none;'})}}

The styles attribute must be a dictionary, and it allows two possible keys: CODE for the style of the actual code, and LINENUMBERS for the style of the left column, which contains the line numbers. Mind that these styles completely replace the default styles and are not simply added to them.

COL

COL

>>> print COL('a','b')
<col>ab</col>

COLGROUP

COLGROUP

>>> print COLGROUP('a','b')
<colgroup>ab</colgroup>

DIV

All helpers apart from XML are derived from DIV and inherit its basic methods.

FORM

FORM

This is one of the most important helpers. In its simple form, it just makes a <form>...</form> tag, but because helpers are objects and have knowledge of what they contain, they can process submitted forms (for example, perform validation of the fields). This will be discussed in detail in Chapter 7.

I

INPUT

INPUT

Creates an <input.../> tag. An input tag may not contain other tags, and is closed by /> instead of >. The input tag has an optional attribute _type that can be set to "text" (the default), "submit", "checkbox", or "radio".

It also takes an optional special argument called "value", distinct from "_value". The latter sets the default value for the input field; the former sets its current value. For an input of type "text", the former overrides the latter:

Simply including a link to an image, a videos or an audio files without markup result in the corresponding image, video or audio file beling included automatically (for audio and video it uses html <audio> and <video> tags).

Adding a link with the qr: prefix such as

qr:http://web2py.com

results in the corresponding QR code being embedded and linking the said URL.

Adding a link tith the embed: prefix such as

embed:http://www.youtube.com/embed/x1w8hKTJ2Co

results in the page being embedded, in this case a youtube video is embedded.

Images can also be embedded with the following syntax:

[[image-description http://.../image.png right 200px]]

Unordered lists with:

- one
- two
- three

Ordered lists with:

+ one
+ two
+ three

and tables with:

----------
X | 0 | 0
0 | X | 0
0 | 0 | 1
----------

The MARKMIN syntax also supports blockquotes, HTML5 audio and video tags, image alignment, custom css, and it can be extended:

Custom blocks are delimited by ``...``:<key> and they are rendered by the function passed as value for the corresponding key in the extra dictionary argument of MARKMIN. Mind that the function may need to escape the output to prevent XSS.

Custom helpers

TAG

Arguments "a", "b", and "d" are automatically escaped; use the XML helper to suppress this behavior. Using TAG you can generate HTML/XML tags not already provided by the API. TAGs can be nested, and are serialized with str(). An equivalent syntax is:

{{=TAG['name']('a', 'b', c='d')}}

If the TAG object is created with an empty name, it can be used to concatenate multiple strings and HTML helpers together without inserting them into a surrounding tag, but this use is deprecated. Use the CAT helper instead.

Notice that TAG is an object, and TAG.name or TAG['name'] is a function that returns a temporary helper class.

MENU

MENU

The MENU helper takes a list of lists or of tuples of the form of response.menu (as described in Chapter 4) and generates a tree-like structure using unordered lists representing the menu. For example:

_class: defaults to "web2py-menu web2py-menu-vertical" and sets the class of the outer UL elements.

ul_class: defaults to "web2py-menu-vertical" and sets the class of the inner UL elements.

li_class: defaults to "web2py-menu-expand" and sets the class of the inner LI elements.

mobile

MENU takes an optional argument mobile. When set to True instead of building a recursive UL menu structure it returns a SELECT dropdown with all the menu options and a onchange attribute that redirects to the page corresponding to the selected option. This is designed an an alterantive menu representation that increases usability on small mobile devices such as phones.

Normally the menu is used in a layout with the following syntax:

{{=MENU(response.menu, mobile=request.user_agent().is_mobile)}}

In this way a mobile device is automatically detected and the menu is rendered accordingly.

BEAUTIFY

BEAUTIFY is used to build HTML representations of compound objects, including lists, tuples and dictionaries:

{{=BEAUTIFY({"a": ["hello", XML("world")], "b": (1, 2)})}}

BEAUTIFY returns an XML-like object serializable to XML, with a nice looking representation of its constructor argument. In this case, the XML representation of:

Server-side DOM'' and parsing

elements

The DIV helper and all derived helpers provide the search methods element and elements.

element returns the first child element matching a specified condition (or None if no match).

elements returns a list of all matching children.

element and elements use the same syntax to specify the matching condition, which allows for three possibilities that can be mixed and matched: jQuery-like expressions, match by exact attribute value, match using regular expressions.

The un-named argument of elements is a string, which may contain: the name of a tag, the id of a tag preceded by a pound symbol, the class preceded by a dot, the explicit value of an attribute in square brackets.

Any attribute can be used to locate an element (not just id and class), including multiple attributes (the function element can take multiple named arguments), but only the first matching element will be returned.

Using the jQuery syntax "div#target" it is possible to specify multiple search criteria separated by a space:

When the view is called, the extended (layout) view is loaded, and the calling view replaces the {{include}} directive inside the layout. Processing continues recursively until all extend and include directives have been processed. The resulting template is then translated into Python code. Note, when an application is bytecode compiled, it is this Python code that is compiled, not the original view files themselves. So, the bytecode compiled version of a given view is a single .pyc file that includes the Python code not just for the original view file, but for its entire tree of extended and included views.

extend, include, block and super are special template directives,
not Python commands.

Any content or code that precedes the {{extend ...}} directive will be inserted (and therefore executed) before the beginning of the extended view's content/code. Although this is not typically used to insert actual HTML content before the extended view's content, it can be useful as a means to define variables or functions that you want to make available to the extended view. For example, consider a view "index.html":

Because the sidebar_enabled assignment in "index.html" comes before the extend, that line gets inserted before the beginning of "layout.html", making sidebar_enabled available anywhere within the "layout.html" code (a somewhat more sophisticated version of this is used in the welcome app).

It is also worth pointing out that the variables returned by the controller function are available not only in the function's main view, but in all of its extended and included views as well.

The argument of an extend or include (i.e., the extended or included view name) can be a python variable (though not a python expression). However, this imposes a limitation -- views that use variables in extend or include statements cannot be bytecode compiled. As noted above, bytecode compiled views include the entire tree of extended and included views, so the specific extended and included views must be known at compile time, which is not possible if the view names are variables (whose values are not determined until run time). Because bytecode compiling views can provide a significant speed boost, using variables in extend and include should generally be avoided if possible.

In some cases, an alternative to using a variable in an include is simply to place regular {{include ...}} directives inside an if...else block.

The above code does not present any problem for bytecode compilation because no variables are involved. Note, however, that the bytecode compiled view will actually include the Python code for both "this_view.html" and "that_view.html", though only the code for one of those views will be executed, depending on the value of some_condition.

Keep in mind, this only works for include -- you cannot place {{extend ...}} directives inside if...else blocks.

response.menu

menu

response.meta

meta

Layouts are used to encapsulate page commonality (headers, footers, menus), and though they are not mandatory, they will make your application easier to write and maintain. In particular, we suggest writing layouts that take advantage of the following variables that can be set in the controller. Using these well known variables will help make your layouts interchangeable:

Except for menu and files, these are all strings and their meaning should be obvious.

response.menu menu is a list of 3-tuples or 4-tuples. The three elements are: the link name, a boolean representing whether the link is active (is the current link), and the URL of the linked page. For example:

response.files is a list of CSS and JS files that are needed by your page.

We also recommend that you use:

{{include 'web2py_ajax.html'}}

in the HTML head, since this will include the jQuery libraries and define some backward-compatible JavaScript functions for special effects and Ajax. "web2py_ajax.html" includes the response.meta tags in the view, jQuery base, the calendar datepicker, and all required CSS and JS response.files.

Default page layout

superfish

ez.css

Below is the "views/layout.html" that ships with the web2py scaffolding application welcome (stripped down of some optional parts). Any new application will have a similar default layout:

There are a few features of this default layout that make it very easy to use and customize:

It is written in HTML5 and uses the "modernizr" [modernizr] library for backward compatibility. The actual layout include some extra conditional statements required by IE and they are omitted for brevity.

It displays both response.title and response.subtitle which can be set in a model. If they are not set, it adopts the application name as title

It includes the web2py_ajax.html file in the header which generated all the link and script import statements.

It uses a modified version of "skeleton" [skeleton] a library for flexible layouts which works on mobile devices and re-arranges columns to fit small screened.

It uses "superfish.js" for dynamic cascading menus. There is an explicit script to activate the superfish cascading menu and it can be removed if not necessary.

The {{=auth.navbar(...)}} displays a welcome to the current user and links to auth functions like login, logout, register, change password, etc. depending on context. It is a heler factory and its output can be manipulated as any other helper. It is placed in a {{try:}}...{{except:pass}} in case auth is undefined.

The {{=MENU(response.menu) displays the menu structure as <ul>...</ul>.

{{include}} is replaced by the content of the extending view when the page is rendered.

By default it uses a conditional three column (the left and right sidebars can be turned off by the extending views)

It uses the following classes: header, main, footer

It contains the following blocks: statusbar, left_sidebar, center, right_sidebar, footer.

Of course you can also completely replace the "layout.html" and "web2py.css" files with your own.

Mobile development

The default layout.html is designed to be friendly to mobile devices but that is not enought. One may need to use different views when a page is visited by a mobile device.

To make developing for desktop and mobile devices easier, web2py includes the @mobilize decorator. This decorator is applied to actions that should have a normal view and a mobile view. This is demonstrated here:

Notice that the decorator must be important once before using it in a controller. When the "index" function is called from a regular browser (desktop computer), web2py will render the returned dictionary using the view "[controller]/index.html". However, when it is called by a mobile device, the dictionary will be rendered by "[controller]/index.mobile.html". Notice that mobile views have the "mobile.html" extension.

Alternatively you can apply the following logic to make all views mobile friendly:

if request.user_agent().is_mobile:
response.view.replace('.html','.mobile.html')

The task of creating the "*.mobile.html" views is left to the developer but we strongly suggest using the "jQuery Mobile" plugin which makes the task very easy.

Notice the function is defined before the {{extend...}} statement -- this results in the function being created before the "layout.html" code is executed, so the function can be called anywhere within "layout.html", even before the {{include}}. Also notice the function is included in the extended view without the = prefix.

Notice that the function is defined in HTML (although it could also contain Python code) so that response.write is used to write its content (the function does not return the content). This is why the layout calls the view function using {{mysidebar()}} rather than {{=mysidebar()}}. Functions defined in this way can take arguments.

Blocks in views

block

Another way to make a view more modular is by using {{block...}}s and this mechanism is an alternative to the mechanism discussed in the previous section.

You can have many blocks, and if a block is present in the extended view but not in the extending view, the content of the extended view is used. Also, notice that unlike with functions, it is not necessary to define blocks before the {{extend ...}} -- even if defined after the extend, they can be used to make substitutions anywhere in the extended view.

super

Inside a block, you can use the expression {{super}} to include the content of the parent. For example, if we replace the above extending view with: