Advanced Page Templates

In the section Using Zope Page Templates you learned the basic features of Page Templates. In this section you’ll learn about advanced techniques including new types of expressions.

Implementation Note

In this documentation, you will occasionally see boxes like this one. They contain extra information specific to one or more implementations of Page Templates.


When this document refers to the “page template implementation,”, “ZPT implementation” or the “implementation”, it means the software that parses and renders page templates to produce output. This could be a framework like Zope, or it could a separate library like zope.pagetemplate. For more information, see Page Template Implementations.

Advanced TAL

In this section we’ll go over all TAL statements and their various options in depth. This material is covered more concisely in TAL Reference.

In this chapter, the terms ‘tag’ and ‘element’ are used in the sense laid out by the XHTML spec. <p> is a tag, while the entire block <p>stuff</p> from opening tag through the closing tag is an element.

Advanced Content Insertion

You’ve already seen how ‘tal:content’ and ‘tal:replace’ work in the chapter entitled Using Zope Page Templates. In this section you’ll learn some advanced tricks for inserting content.

Inserting Structure

Normally, the ‘tal:replace’ and ‘tal:content’ statements convert HTML tags and entities in the text that they insert into an “escaped” form that appears in the resulting document as plain text rather than HTML markup. For instance, the ‘<’ character is “escaped” to ‘&amp;lt;’. If you want to insert text as part of the HTML structure of your document, avoiding this conversion , you need to precede the expression with the ‘structure’ keyword.

This feature is useful when you are inserting a fragment of HTML or XML that is stored by an object or generated by another object. For instance, you may have news items that contain simple HTML markup such as bold and italic text when they are rendered, and you want to preserve this when inserting them into a “Top News” page. In this case, you might write:

<p tal:repeat="newsItem context/topNews"
   tal:content="structure newsItem">
  A news item with<code>HTML</code> markup.

This will insert the news items’ HTML into a series of paragraphs. The built-in variable ‘context’ refers to the folder in which the template is rendered; See the “Expressions” section further below in this chapter for more information on ‘context’. In this case, we use ‘context’ as the starting point for finding the object ‘topNews’, which is presumably a list of news items or a Script which fetches such a list.

The ‘structure’ keyword prevents the text of each newsItem value from being escaped. It doesn’t matter whether the text actually contains any HTML markup, since ‘structure’ really means “leave this text alone”. This behavior is not the default because most of the text that you insert into a template will not contain HTML, but may contain characters that would interfere with the structure of your page.

Dummy Elements

You can include page elements that are visible in the template but not in generated text by using the built-in variable ‘nothing’, like this:

<tr tal:replace="nothing">
  <td>10213</td><td>Example Item</td><td>$15.34</td>

This can be useful for filling out parts of the page that will be populated with dynamic content. For instance, a table that usually has ten rows will only have one row in the template. By adding nine dummy rows, the template’s layout will look more like the final result.

Default Content

You can leave the contents of an element alone by using the ‘default’ expression with ‘tal:content’ or ‘tal:replace’. For example:

<p tal:content="default">Spam</p>

This renders to:


Most often you will want to selectively include default content, rather than always including it. For example:

<p tal:content="python:context.getFood() or default">Spam</p>

Advanced Repetition

You’ve already seen most of what you can do with the ‘tal:repeat’ statement in the section entitled Using Zope Page Templates. This section covers a few advanced features of the ‘tal:repeat’ statement.

Repeat Variables

One topic that bears more explanation are repeat variables. Repeat variables provide information about the current repetition. For a full list of the available attributes, see tal:repeat documentation.

You can access the contents of a repeat variable using path expressions or Python expressions. In path expressions, you write a three-part path consisting of the name ‘repeat’, the statement variable’s name, and the name of the information you want, for example, ‘repeat/item/start’. In Python expressions, you use normal dictionary notation to get the repeat variable, then attribute access to get the information, for example, ‘python:repeat[‘item’].start’. The reason that you can’t simply write ‘repeat/start’ is that ‘tal:repeat’ statements can be nested, so you need to be able to specify which one you want information about.

Repetition Tips

Here are a couple practical tips that you may find useful. Sometimes you’d like to repeat part of your template, but there is no naturally enclosing element. In this case, you must add an enclosing element, but you want to prevent it from appearing in the rendered page. You can do this with the ‘tal:omit-tag’ statement:

<div tal:repeat="section context/getSections"
  <h4 tal:content="section/title">Title</h4>
  <p tal:content="section/text">quotation</p>

This is not just a matter of saving a few characters in the rendered output. Including the ‘div’ tags in the output could affect the page layout, especially if it has stylesheets. We use the tal ‘omit-tag’ statement to disinclude the ‘div’ tag (and its pair closing tag) while leaving its contents unmolested. The ‘tal:omit-tag’ statement is described in more detail later in this chapter.

While it’s been mentioned before, it’s worth saying again: you can nest ‘tal:repeat’ statements inside each other. Each ‘tal:repeat’ statement must have a different repeat variable name. Here’s an example that shows a math times-table:

<table border="1">
  <tr tal:repeat="x python:range(1, 13)">
    <td tal:repeat="y python:range(1, 13)"
        tal:content="python:'%d x %d = %d' % (x, y, x*y)">
        X x Y = Z

This example uses Python expressions, which are covered later in this section.

One useful feature that isn’t supplied by ‘tal:repeat’ is sorting. If you want to sort a list you can do so i n a Python expression:

<table tal:define="objects context/objectValues;
                   sorted_objects python:sorted(objects)">
  <tr tal:repeat="item sorted_objects">
    <td tal:content="item/title">title</td>

Advanced Attribute Control

You’ve already met the ‘tal:attributes’ statement. You can use it to dynamically replace tag attributes, for example, the ‘href’ attribute on an ‘a’ element. You can replace more than one attribute on a tag by separating attributes with semicolons. For example, the code below will generate an “href” and a “class” attribute:

<a href="link"
   tal:attributes="href context/getLink;
                   class context/getClass">link</a>

You can also define attributes with XML namespaces. For example:

    dc:Creator="creator name"
    tal:attributes="dc:Creator context/owner/getUserName">

Simply put the XML namespace prefix before the attribute name and you can create attributes with XML namespaces.

Defining Variables

You can define your own variable using the ‘tal:define’ attribute. There are several reasons that you might want to do this. One reason is to avoid having to write long expressions repeatedly in a template. Another is to avoid having to call expensive methods repeatedly. You can define a variable once within an element on a tag and then use it many times within elements which are enclosed by this tag. For example, here’s a list that defines a variable and later tests it and repeats over it:

<ul tal:define="items container/objectIds"
  <li tal:repeat="item items">
    <p tal:content="item">id</p>

The ‘tal:define’ statement creates the variable ‘items’, which you can use anywhere in the ‘ul’ element. Notice also how you can have two TAL statements on the same ‘ul’ tag. See the section “Interactions Between TAL Statements” later in this chapter for more information about using more than one statement on a tag. In this case the first statement assigns the variable ‘items’ and the second uses ‘items’ in a condition to see whether it is false (in this case, an empty sequence) or true. If the ‘items’ variable is false, then the ‘ul’ element is not shown.

Now, suppose that instead of simply removing the list when there are no items, you want to show a message. To do this, place the following before the list:

<h4 tal:condition="not:container/objectIds">
  There Are No Items

The expression, ‘not:container/objectIds’ is true when ‘container/objectIds’ is false, and vice versa. See the section, “Not Expressions” later in this chapter for more information.

You can’t use your ‘items’ variable here, because it isn’t defined yet. If you move the definition of ‘items’ to the ‘h4’ element, then you can’t use it in the ‘ul’ element any more, because it becomes a local variable of the ‘h4’ element. To have it available on both tags, you can place the definition on some element that encloses both the ‘h4’ and the ‘ul’ for example the ‘body’.

You can define more than one variable using ‘tal:define’ by separating them with semicolons. For example:

<p tal:define="ids container/objectIds;
               title container/title">

You can define as many variables as you wish. Each variable can have its own global or local scope. You can also refer to earlier defined variables in later definitions. For example:

<p tal:define="title template/title;
               untitled not:title;
               tlen python:len(title);">

With judicious use of ‘tal:define’ you can improve the efficiency and readability of your templates.

Omitting Tags

You can remove tags with the ‘tal:omit-tag’ statement. You will seldom need to use this TAL statement, but occasionally it’s useful. The omit-tag attribute removes opening and closing tags, but does not affect the contents of the element. For example:

<b tal:omit-tag=""><i>this</i> stays</b>

Renders to:

<i>this</i> stays

At this level of usage, ‘tal:omit-tag’ operates almost like ‘tal:replace=”default”’. However, ‘tal:omit-tag’ can also be used with a true/false expression, in which case it only removes the tags if the expression is true. For example:

Friends: <span tal:repeat="friend friends">
  <b tal:omit-tag="not:friend/best"

This will produce a list of friends, with our “best” friend’s name in bold.

Error Handling

If an error occurs in your page template, you can catch that error and show a useful error message to your user. For example, suppose your template defines a variable using form data:

<span tal:define="prefs request/form/prefs"
      tal:omit-tag="" />

If the template implementation encounters a problem, like not being able to find the ‘prefs’ variable in the form data, the entire page will break; you’ll get an error page instead. Happily, you can avoid this kind of thing with limited error handling using the ‘tal:on-error’ statement:

<span tal:define="prefs context/scriptToGetPreferences"
      tal:on-error="string:An error occurred">

When an error is raised while rendering a template, page templates looks for a ‘tal:on-error’ statement to handle the error. It first looks in the current element, then on its enclosing element, and so on until it reaches the top-level element. When it finds an error handler, it replaces the contents of that element with the error handling expression. In this case, the ‘span’ element will contain an error message.

Typically you’ll define an error handler on an element that encloses a logical page element, for example a table. If an error crops up drawing the table, then the error handler can simply omit the table from the page, or else replace it with an error message of some sort.

For more flexible error handling you can call a script. For example:

<div tal:on-error="structure context/handleError">

Any error that occurs inside the ‘div’ will call the ‘handleError’ script. Note that the ‘structure’ option allows the script to return HTML. Your error handling script can examine the error and take various actions depending on the error. Your script gets access to the error through the ‘error’ variable in the namespace. For example:

## Script (Python) "handleError"
##bind namespace=_
if error.type==ZeroDivisionError:
    return "<p>Can't divide by zero.</p>"
    return """<p>An error occurred.</p>
              <p>Error type: %s</p>
              <p>Error value: %s</p>""" % (error.type,

Your error handling script can take all kinds of actions, for example, it might log the error by sending email.

The ‘tal:on-error’ statement is not meant for general purpose exception handling. For example, you shouldn’t validate form input with it. You should use a script for that, since scripts allow you to do powerful exception handling. The ‘tal:on-error’ statement is for dealing with unusual problems that can occur when rendering templates.

Interactions Between TAL Statements

When there is only one TAL statement per element, the order in which they are executed is simple. Starting with the root element, each element’s statements are executed, then each of its child elements are visited, in order, and their statements are executed, and so on.

However, it’s possible to have more than one TAL statement on the same element. Any combination of statements may appear on the same element, except that the ‘tal:content’ and ‘tal:replace’ statements may not appear together.

When an element has multiple statements, they are executed in this order:

  1. define
  2. condition
  3. repeat
  4. content or replace
  5. attributes
  6. omit-tag

Since the ‘tal:on-error’ statement is only invoked when an error occurs, it does not appear in the list.

The reasoning behind this ordering goes like this: you often want to set up variables for use in other statements, so define comes first. The very next thing to do is decide whether this element will be included at all, so condition is next; since the condition may depend on variables you just set, it comes after define. It is valuable to be able to replace various parts of an element with different values on each iteration of a repeat, so repeat comes before content, replace and attributes. Content and replace can’t both be used on the same element so they occur at the same place. Omit-tag comes last since no other statements are likely to depend on it and since it should come after define and repeat.

Here’s an example element that includes several TAL statements:

<p tal:define="x /root/a/long/path/x | nothing"
   tal:attributes="class x/class">Ex Text</p>

Notice how the ‘tal:define’ statement is executed first, and the other statements rely on its results.

There are three limits you should be aware of when combining TAL statements on elements:

  1. Only one of each kind of statement can be used on a single tag. Since HTML does not allow multiple attributes with the same name. For example, you can’t have two ‘tal:define’ on the same tag.
  2. Both of ‘tal:content’ and ‘tal:replace’ cannot be used on the same tag, since their functions conflict.
  3. The order in which you write TAL attributes on a tag does not affect the order in which they execute. No matter how you arrange them, the TAL statements on a tag always execute in the fixed order described earlier.

If you want to override the ordering of TAL statements, you must do so by enclosing the element in another element and placing some of the statements on this new element. For example suppose you want to loop over a series of items but skip some. Here’s an attempt to write a template that loops over the numbers zero to nine and skips three:

<!-- broken template -->
  <li tal:repeat="n python:range(10)"
      tal:condition="python:n != 3"

This template doesn’t work due to TAL statement execution order. Despite the order in which they are written, the condition is always tested before the repeat is executed. This results in a situation in which the ‘n’ variable is not defined until after it is tested, which ultimately causes an error when you attempt to test or otherwise view the template. Here’s a way around this problem:

  <div tal:repeat="n python:range(10)"
    <li tal:condition="python:n != 3"

This template solves the problem by defining the ‘n’ variable on an enclosing ‘div’ element. Notice that the ‘div’ tag will not appear in the output due to its ‘tal:omit-tag’ statement.

Although ‘span’ and ‘div’ are natural choices for this in HTML, there is, in general, no equivalent natural element in XML. In this case, you can use TAL’s namespace in a new way: while TAL does not define any tags, it doesn’t prohibit any either. You can make up any tag name you like within the TAL namespace, and use it to make an element, like so:

<tal:series define="items context/getItems">
  <tal:items repeat="item items">
      <tal:parts repeat="part item">
        <p tal:content="part">Part</p>
  <p tal:condition="not:items">No parts!</p>

The ‘tal:series’, ‘tal:items’, and ‘tal:parts’ tags in this example should be acceptable to tools that handle XML namespaces properly, and to many HTML tools. This method has two additional advantages over a ‘div’. First, TAL tags are omitted just like TAL attributes, so no ‘tal:omit-tag’ is necessary. Second, TAL attributes in these tags don’t require their own ‘tal:’ prefix, since they inherit the namespace of the tag. The METAL namespace can be used in exactly the same fashion.


You’ve already encountered Page Template expressions. Expressions provide values to template statements. For example, in the TAL statement ‘<td tal:content=”request/form/age”>Age</td>’, the expression of the statement is ‘request/form/age’. ‘request/form/age’ is an example of a path expression. Path expressions describe objects by giving them paths such as ‘request/form/age’, or ‘user/getUserName’. Expressions only work in the context of a TAL statement; they do not work in “normal” HTML inserted in your page templates. In this section you’ll learn about all the different types of expressions, and variables.

Built-in Page Template Variables

Variables are names that you can use in expressions. You have already seen some examples of the built-in variables such as ‘template’, ‘user’, ‘repeat’, and ‘request’. Here is the complete list of the other built-in variables and their uses.

See Built-in Names for more information.

A false value, similar to a blank string, that you can use in ‘tal:replace’ or ‘tal:content’ to erase an element or its contents. If you set an attribute to ‘nothing’, the attribute is removed from the tag (or not inserted). A blank string, on the other hand, would insert the tag with an empty value, as in ‘alt=”“’.
A special value that doesn’t change anything when used in ‘tal:replace’, ‘tal:content’, or ‘tal:attributes’. It leaves the template text in place.
The keyword arguments, if any, that were passed to the template. When a template is rendered from the web, no options are present. Options are only available when a template is called from Python or by similarly complex means. For example, when the template ‘t’ is called by the Python expression ‘t(foo=1)’, the path ‘options/foo’ equals ‘1’.
A dictionary of attributes of the current tag in the template. The keys are the attributes names, and the values are the original values of the attributes in the template. This variable is rarely needed.

Zope Implementation Note

The following names are optional names supported by Zope, but are not required by the TALES standard.

The root Zope object. Use this to get Zope objects
from fixed locations, no matter where your template is placed or called.
The object on which the template is being called.
This is often the same as the container, but can be different if you are using acquisition. Use this to get Zope objects that you expect to find in different places depending on how the template is called.
The container (usually a Folder) in which the
template is kept. Use this to get Zope objects from locations relative to the template’s permanent home. The ‘container’ and ‘context’ variables refer to the same object when a template is called from its normal location. However, when a template is applied to another object (for example, a ZSQL Method) the ‘container’ and ‘context’ will not refer to the same object.
The collection of Python modules available to
templates. See the section on writing Python expressions.

You’ll find examples of how to use these variables throughout this chapter.

String Expressions

String expressions allow you to easily mix path expressions with text. All of the text after the leading ‘string:’ is taken and searched for path expressions. Each path expression must be preceded by a dollar sign (‘$’). Here are some examples:

"string:Just text. There's no path here."
"string:copyright $year by Fred Flintstone."

If the path expression has more than one part (if it contains a slash), or needs to be separated from the text that follows it, it must be surrounded by braces (‘{}’). For example:

"string:Three ${vegetable}s, please."
"string:Your name is ${user/getUserName}!"

Notice how in the example above, you need to surround the ‘vegetable’ path with braces so that the implementation doesn’t mistake it for ‘vegetables’.

Since the text is inside of an attribute value, you can only include a double quote by using the entity syntax ‘&quot;’. Since dollar signs are used to signal path expressions, a literal dollar sign must be written as two dollar signs (‘$$’). For example:

"string:Please pay $$$dollars_owed"
"string:She said, &quot;Hello world.&quot;"

Some complex string formatting operations (such as search and replace or changing capitalization) can’t easily be done with string expressions. For these cases, you should use Python expressions or Scripts.

Path Expressions

Path expressions refer to objects with a path that resembles a URL path. A path describes a traversal from object to object. All paths begin with a known object (such as a built-in variable, a repeat variable, or a user defined variable) and depart from there to the desired object. Here are some example paths expressions:


With path expressions you can traverse from an object to its sub-objects including properties and methods.

Zope Implementation Note

Zope restricts object traversal in path expressions in the same way that it restricts object access via URLs. You must have adequate permissions to access an object in order to refer to it with a path expression.

Alternate Paths

The path ‘template/title’ is guaranteed to exist every time the template is used, although it may be a blank string. Some paths, such as ‘request/form/x’, may not exist during some renderings of the template. This normally causes an error when the zpt implementation evaluates the path expression.

When a path doesn’t exist, you may have a fall-back path or value that you would like to use instead. For instance, if ‘request/form/x’ doesn’t exist, you might want to use ‘context/x’ instead. You can do this by listing the paths in order of preference, separated by vertical bar characters (‘|’):

<h4 tal:content="request/form/x | context/x">Header</h4>

Two variables that are very useful as the last path in a list of alternates are ‘nothing’ and ‘default’. For example, ‘default’ tells ‘tal:content’ to leave the dummy content. Different TAL statements interpret ‘default’ and ‘nothing’ differently. See TAL Reference for more information.

You can also use a non-path expression as the final part in an alternate-path expression. For example:

<p tal:content="request/form/age|python:18">age</p>

In this example, if the ‘request/form/age’ path doesn’t exist, then the value is the number 18. This form allows you to specify default values to use which can’t be expressed as paths. Note, you can only use a non-path expression as the last alternative.

You can also test the existence of a path directly with the exists expression type prefix. See the section “Exists Expressions” below for more information on exists expressions.

Not Expressions

Not expressions let you negate the value of other expressions. For example:

<p tal:condition="not:context/objectIds">
  There are no contained objects.

Not expressions return true when the expression they are applied to is false, and vice versa. In Python, zero, empty strings, empty sequences, nothing, and None are considered false, while everything else is true (unless it implements a custom __bool__ method). Non-existent paths are neither true nor false, and applying a ‘not:’ to such a path will fail.

There isn’t much reason to use not expressions with Python expressions since you can use the Python ‘not’ keyword instead.

Nocall Expressions

An ordinary path expression tries to render the object that it fetches. This means that if the object is a function, Script, Method, or some other kind of executable thing, then the expression will evaluate to the result of calling the object. This is usually what you want, but not always. For example, if you want to put a page template into a variable so that you can refer to its properties, you can’t use a normal path expression because it will render the template into a string.

If you put the ‘nocall:’ expression type prefix in front of a path, it prevents the rendering and simply gives you the object. For example:

<span tal:define="page nocall:context/aPage"
      tal:content="string:${page/getId}: ${page/title}">
Id: Title</span>

This expression type is also valuable when you want to define a variable to hold a function or class from a module, for use in a Python expression.

Nocall expressions can also be used on functions, rather than objects:

<p tal:define="join nocall:modules/string/join">

This expression defines the ‘join’ variable as a function (‘string.join’), rather than the result of calling a function.

Exists Expressions

An exists expression is true if its path exists, and otherwise is false. For example here’s one way to display an error message only if it is passed in the request:

<h4 tal:define="err request/form/errmsg | nothing"

You can do the same thing more easily with an exists expression:

<h4 tal:condition="exists:request/form/errmsg"

You can combine exists expressions with not expressions, for example:

<p tal:condition="not:exists:request/form/number">Please enter
a number between 0 and 5</p>

Note that in this example you can’t use the expression, “not:request/form/number”, since that expression will be true if the ‘number’ variable exists and is zero.

Python Expressions

The Python programming language is a simple and expressive one. If you have never encountered it before, you should read one of the excellent tutorials or introductions available at the Python website.

A Page Template Python expression can contain anything that the Python language considers an expression. You can’t use statements such as ‘if’ and ‘while’.

Zope Implementation Note

In addition, Zope imposes some security restrictions to keep you from accessing protected information, changing secured data, and creating problems such as infinite loops.


One place where Python expressions are practically necessary is in ‘tal:condition’ statements. You usually want to compare two strings or numbers, and there is no support in TAL to do this without Python expressions. In Python expressions, you can use the comparison operators ‘<’ (less than), ‘>’ (greater than), ‘==’ (equal to), and ‘!=’ (not equal to). You can also use the boolean operators ‘and’, ‘not’, and ‘or’. For example:

<p tal:repeat="widget widgets">
  <span tal:condition="python:widget.type == 'gear'">
  Gear #<span tal:replace="repeat/widget/number">1</span>:
  <span tal:replace="widget/name">Name</span>

This example loops over a collection of objects, printing information about widgets which are of type ‘gear’.

Sometimes you want to choose different values inside a single statement based on one or more conditions. You can do this with the and and or operators, like this:

You <span tal:define="name user/getUserName"
     tal:replace="python:name=='Anonymous User' and
                         'need to log in' or default">
      are logged in as
      <span tal:replace="name">Name</span>

If the user is ‘Anonymous’, then the ‘span’ element is replaced with the text ‘need to log in’. Otherwise, the default content is used, which is in this case ‘are logged in as …’.

This operator combinaion works like an if/then/else statement. Here’s another example of how you can use this pattern:

<tr tal:define="oddrow repeat/item/odd"
    tal:attributes="class python:oddrow and 'oddclass' or 'evenclass'">

This assigns ‘oddclass’ and ‘evenclass’ class attributes to alternate rows of the table, allowing them to be styled differently in HTML output, for example.

Without this pattern you could also write two ‘tr’ elements with different conditions, one for even rows, and the other for odd rows.

Using other Expression Types

You can use other expression types inside of a Python expression. Each expression type has a corresponding function with the same name, including: ‘path()’, ‘string()’, ‘exists()’, and ‘nocall()’. This allows you to write expressions such as:

"python:path('context/%s/thing' % foldername)"
"python:path('request/form/x') or default"

The final example has a slightly different meaning than the path expression, “request/form/x | default”, since it will use the default text if “request/form/x” doesn’t exists or if it is false.

Python Modules

The Python language comes with a large number of modules, which provide a wide variety of capabilities to Python programs. Each module is a collection of Python functions, data, and classes related to a single purpose, such as mathematical calculations or regular expressions.

Several modules, including “math” and “string”, are available in Python expressions by default. For example, you can get the value of pi from the math module by writing “python:math.pi”. To access it from a path expression, however, you need to use the ‘modules’ variable, “modules/math/pi”.

The “string” module is hidden in Python expressions by the “string” expression type function, so you need to access it through the ‘modules’ variable. You can do this directly in an expression in which you use it, or define a variable for it, like this:

tal:define="mstring modules/string"
tal:replace="python:mstring.join(slist, ':')"

In practice you’ll rarely need to do this since you can use string methods most of the time rather than having to rely on functions in the string module.


This section covers some useful and some obscure nooks and crannies of Page Templates, and after reading it you may feel a bit overwhelmed. Don’t worry, you don’t need to know everything in this chapter to effectively use Page Templates. You should understand the different path types and macros, but you can come back to the rest of the material when you need it. The advanced features that you’ve learned about in this chapter are there for you if and when you need them.