Templates

A template is a file on disk which can be used to render dynamic data provided by a view, usually surrounded by information that is static. repoze.bfg offers a number of ways to perform templating tasks “out of the box”, and provides alternative templating language support via add-on “bindings” packages.

Templating With Chameleon ZPT Page Templates

Like Zope, repoze.bfg uses Zope Page Templates (ZPT) as its default and best-supported templating language. However, repoze.bfg uses a different implementation of the ZPT specification than Zope does: the Chameleon chameleon.zpt templating engine. This templating engine complies largely with the Zope Page Template template specification, however it is significantly faster.

Note

The language definition documentation for Chameleon ZPT-style templates is available from the Chameleon website. See its documentation for the Chameleon ZPT language specification.

Given that there is a chameleon.zpt template named foo.pt in a directory in your application named templates, you can render the template from a view like so:

1
2
3
from repoze.bfg.chameleon_zpt import render_template_to_response
def sample_view(context, request):
    return render_template_to_response('templates/foo.pt', foo=1, bar=2)

The first argument to render_template_to_response shown above (and its sister function render_template, not shown, which just returns a string body) is the template path. In the example above, the path templates/foo.pt is relative. Relative to what, you ask? Relative to the directory in which the views.py file which names it lives, which is usually the repoze.bfg application’s package directory.

Although a path is usually just a simple relative pathname, a path passed to render_template_to_response can be absolute, starting with a slash on UNIX or a drive letter prefix on Windows. The path can alternately be a resource “specification” in the form some.dotted.package_name:relative/path, making it possible to address template resources which live in another package.

render_template_to_response always returns a Response object which has a status code of 200 OK and a content-type of text-html. If you need more control over the status code and content-type, either set attributes on the response that this function returns or use the render_template function instead (see repoze.bfg.chameleon_zpt for the details), which also renders a ZPT template but returns a string instead of a Response. You can use the string manually as a response body. Here’s an example of using render_template:

1
2
3
4
5
6
7
from repoze.bfg.chameleon_zpt import render_template
from webob import Response
def sample_view(context, request):
    result = render_template('templates/foo.pt', foo=1, bar=2)
    response = Response(result)
    response.content_type = 'text/plain'
    return response

Here’s an example of using render_template_to_response but changing the content-type and status:

1
2
3
4
5
6
from repoze.bfg.chameleon_zpt import render_template_to_response
def sample_view(context, request):
    response = render_template_to_response('templates/foo.pt', foo=1, bar=2)
    response.content_type = 'text/plain'
    response.status_int = 204
    return response

A Sample Template

Here’s what a simple chameleon.zpt template used under repoze.bfg might look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:tal="http://xml.zope.org/namespaces/tal">
 <head>
     <meta http-equiv="content-type" content="text/html; charset=utf-8" />
     <title>${project} Application</title>
 </head>
   <body>
      <h1 class="title">Welcome to <code>${project}</code>, an
       application generated by the <a
       href="http://static.repoze.org/bfgdocs">repoze.bfg</a> web
       application framework.</h1>
   </body>
 </html>

Note the use of Genshi -style ${replacements} above. This is one of the ways that chameleon.zpt differs from standard ZPT. The above template expects to find a project key in the set of keywords passed in to it via render_template or render_template_to_response. Typical ZPT attribute-based syntax (e.g. tal:content and tal:replace) also works in these templates.

Using ZPT Macros in repoze.bfg

Unlike Zope “browser views”, repoze.bfg doesn’t make any names (such as context or view) available to chameleon.zpt templates by default. Instead, it expects you to pass all the names you need into the template.

One of the common needs in ZPT-based template is to one template’s “macros” from within a different template. In Zope, this is typically handled by retrieving the template from the context. To do the same thing in repoze.bfg, you need to make the macro template itself available to the rendered template by passing template in which the macro is defined (or even the macro itself) into the rendered template. To make a macro available to the rendered template, you can retrieve a different template using the get_template API, and pass it in to the template being rendered. For example:

1
2
3
4
5
6
from repoze.bfg.chameleon_zpt import render_template_to_response
from repoze.bfg.chameleon_zpt import get_template

def my_view(context, request):
    main = get_template('templates/master.pt')
    return render_template_to_response('templates/mytemplate.pt', main=main)

Where templates/master.pt might look like so:

1
2
3
4
5
6
7
8
9
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:tal="http://xml.zope.org/namespaces/tal"
       xmlns:metal="http://xml.zope.org/namespaces/metal">
   <span metal:define-macro="hello">
     <h1>
       Hello <span metal:define-slot="name">Fred</span>!
     </h1>
   </span>
 </html>

And templates/mytemplate.pt might look like so:

1
2
3
4
5
6
7
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:tal="http://xml.zope.org/namespaces/tal"
       xmlns:metal="http://xml.zope.org/namespaces/metal">
   <span metal:use-macro="main.macros['hello']">
     <span metal:fill-slot="name">Chris</span>
   </span>
 </html>

Templating with Chameleon Text Templates

repoze.bfg also allows for the use of templates which are composed entirely of non-XML text via Chameleon. To do so, you can create templates that are entirely composed of text except for ${name} -style substitution points.

Here’s an example usage of a Chameleon text template. Create a file on disk named text.txt in your project’s templates directory with the following contents:

Hello, ${name}!

Then in your project’s views.py module, you can create a view which renders this template:

1
2
3
4
from repoze.bfg.chameleon_text import render_template_to_response

def text_view(context, request):
    return render_template_to_response('templates/text.txt', name='World')

The Chameleon text rendering API is a wholesale mirror of the Chameleon text ZPT rendering API, it’s just imported from another place; see repoze.bfg.chameleon_text for the API description.

Side Effects of Rendering a Chameleon Template

When a Chameleon template is rendered from a file, the templating engine writes a file in the same directory as the template file itself as a kind of cache, in order to do less work the next time the template needs to be read from disk. When using chameleon.core version 1.0b32 and lower, this filename is <template_name>.cache. When using chameleon.core version 1.0b33 and higher, this filename is <template_name>.py. If you see “strange” .py or .cache files showing up in your templates directory, it is due to this feature. If you’re using a version control system such as Subversion, you should cause it to ignore these files. Here’s the contents of my svn propedit svn:ignore . in each of my templates directories. (Note that I always name my Chameleon ZPT template files with a .pt extension, so that this pattern works):

1
2
*.cache
*.pt.py

Automatically Reloading Templates

It’s often convenient to see changes you make to a template file appear immediately without needing to restart the application process. repoze.bfg allows you configure your application development environment so that a change to a template will be automatically detected, and the template will be reloaded on the next rendering.

Warning

auto-template-reload behavior is not recommended for production sites as it slows rendering slightly; it’s usually only desirable during development.

In order to turn on automatic reloading of templates, you can use an environment variable setting or a configuration file setting.

To use an environment variable, start your application under a shell using the BFG_RELOAD_TEMPLATES environment variable set to 1, For example:

$ BFG_RELOAD_TEMPLATES=1 bin/paster serve myproject.ini

To use a setting in the the application .ini file for the same purpose, set the reload_templates key to true within the application’s configuration section, e.g.:

[app:main]
use = egg:MyProject#app
reload_templates = true

Chameleon Template Internationalization

See the internationalization chapter of the Chameleon documentation for information about supporting internationalized units of text within Chameleon templates.

Templating with other Templating Languages

Because view functions are typically the only code in repoze.bfg that need to know anything about templates, and because view functions are very simple Python, you can use whatever templating system you’re most comfortable with within repoze.bfg. Install the templating system, import its API functions into your views module, use those APIs to generate a string, then return that string as the body of a WebOb Response object. Assuming you have Mako installed, here’s an example of using Mako from within a repoze.bfg view:

1
2
3
4
5
6
7
8
from mako.template import Template
from webob import Response

def make_view(context, request):
    template = Template(filename='/templates/template.mak')
    result = template.render(name=context.name)
    response = Response(result)
    return response

Note

It’s reasonably easy to write custom templating system binding packages for use under repoze.bfg. See Available Add-On Template System Bindings for example packages.

Note that if you use third-party templating languages without cooperating BFG bindings, the auto-template-reload strategy explained in Automatically Reloading Templates will not be available, nor will the template resource overriding capability explained in Overriding Resources be available.

Available Add-On Template System Bindings

repoze.bfg.xslt is an add-on which provides XSL template bindings. It lives in the Repoze Subversion repository at http://svn.repoze.org/repoze.bfg.xslt.

repoze.bfg.chameleon_genshi package is an add-on which provides Chameleon Genshi-style template support. It lives in the Repoze Subversion repository at http://svn.repoze.org/repoze.bfg.chameleon_genshi.

Jinja2 template bindings are available for repoze.bfg in the repoze.bfg.jinja2 package. It lives in the Repoze Subversion repository at http://svn.repoze.org/repoze.bfg.jinja2.

Courtesy of Carlos de la Guardia, bindings for the Zope zope.pagetemplate package (“old TAL”) are available from http://svn.repoze.org/repoze.bfg.zopepagetemplate/.