Templates¶
Introduction¶
When you generate HTML from the server (using HTML views) it can be very handy to have a template language available. A template language provides some high-level constructs for generating HTML, which are handy. It can also help you avoid HTML injection security bugs because it takes care of escaping HTML. It may also be useful to separate HTML presentation from code.
This document discusses template rendering on the server. In some modern web applications template rendering is done in the browser instead of on the server. To do client-side template rendering you need to use a Client web framework with Morepath. See also Static resources with Morepath.
Morepath does not have a template language built in. The example in this document uses more.chameleon. more.chameleon integrates the Chameleon template engine, which implements the ZPT template language. If you prefer Jinja2, you can use the more.jinja2 extension instead. You can also integrate other template languages.
To use a template you need to use the template
argument with the
morepath.App.html()
view directive.
Example¶
This example presupposes that more.chameleon and its dependencies have been installed. Here is how we use it:
from more.chameleon import ChameleonApp
class App(ChameleonApp):
pass
@App.template_directory()
def get_template_directory():
return 'templates'
@App.html(model=Person, template='person.pt')
def person_default(self, request):
return { 'name': self.name }
Let’s examine this code. First we import ChameleonApp
and subclass
from it in our own app. This enables Chameleon templating for the
.pt
file extension.
We then need to specify the directory that contains our templates
using the morepath.App.template_directory()
directive. The
directive should return either an absolute or a relative path to this
template directory. If a relative path is returned, it is
automatically made relative to the directory the Python module is in.
Next we use template='person.pt'
in the HTML view
directive. person.pt
is a file sitting in the templates
directory, with this content:
<html>
<body>
<p>Hello ${name}!</p>
</body>
</html>
Once we have this set up, given a person with a name
attribute of
"world"
, the output of the view is the following HTML:
<html>
<body>
<p>Hello world!</p>
</body>
</html>
The template is applied on the return value of the view function and the request. This results in a rendered template that is returned as the response.
Overrides¶
When you subclass an app you may want to override some of the
templates it uses, or add new templates. You can do this by using the
template_directory
directive in your subclassed app:
class SubApp(App):
pass
@SubApp.template_directory()
def get_override_template_directory():
return 'templates_override'
Morepath’s template integration searches for templates in the template
directories in application order, so for SubApp
here, first
templates_override
, and then templates
as defined by the base
App
. So for SubApp
, you can override a template defined in the
directory templates
by placing a file with the same name in the
directory templates_override
. This only affects SubApp
, not
App
itself.
You can also use the before
argument with the
morepath.App.template_directory()
directive to specify more
exactly how you want template directories to be searched. This can be
useful if you want to organize your templates in multiple directories
in the same application. If get_override_template_directory
should
come before get_template_directory
in the directory search path,
you should use before=get_template_directory
:
@SubApp.template_directory(before=get_template_directory)
def get_override_template_directory():
return 'templates_override'
but it is usually simpler not to be this explicit and to rely on application inheritance instead.
Details¶
Templates are loaded during configuration time at startup. The file
extension of the extension (such as .pt
) indicates the template
engine to use.
Morepath itself does not support any template language out of the box, but lets you register a template language engine for a file extension. You can reuse a template language integration in the same way you reuse any Morepath code: by subclassing the app class that implements it in your app.
The template language integration works like this:
- During startup time,
person.pt
is loaded from the configured template directories as a template object. - When the
person_default
view is rendered, its return value is passed into the template, along with the request. The template language integration code then makes this information available for use by the template – the details are up to the integration (and should be documented there).
The template
argument works not just with html
but also with
view
, json
, and any other view functions you may have. It’s
most useful for html
views however.
Integrating a new template engine¶
A template in Morepath is actually just a convenient way to generate a
render
function for a view. That render
function is then used
just like when you write it manually: it’s given the return value of
the view function along with a request object, and should return a
WebOb response.
Here is an example of how you can integrate the Chameleon template engine
for .pt
files (taken from more.chameleon):
import chameleon
@App.template_loader(extension='.pt')
def get_template_loader(template_directories, settings):
settings = settings.chameleon.__dict__.copy()
# we control the search_path entirely by what we pass here as
# template_directories, so we never want the template itself
# to prepend its own path
settings['prepend_relative_search_path'] = False
return chameleon.PageTemplateLoader(
template_directories,
default_extension='.pt',
**settings)
@App.template_render(extension='.pt')
def get_chameleon_render(loader, name, original_render):
template = loader.load(name)
def render(content, request):
variables = {'request': request}
variables.update(content)
return original_render(template.render(**variables), request)
return render
@App.setting_section(section='chameleon')
def get_setting_section():
return {'auto_reload': False}
Some details:
extension
is the file extension. When you refer to a template with a particular extension, this template engine is used.The function decorated by
morepath.App.template_loader()
gets two arguments: directories to look in for templates (earliest in the list first), and Morepath settings from which template engine settings can be extracted.The function decorated by
morepath.App.template_render()
gets three arguments:loader
: the loader constructed by thetemplate_loader
directive.name
: the name of the template to create a render function for.- The
original_render
function as passed into the view decorator, sorender_html
for instance. It takes the content to render and the request and returns a webob response object. then passed along to Chameleon.
The decorated function needs to return a
render
function which takes the content to render (output from view function) and the request as arguments.The implementation of this can use the original
render
function which is passed in as an argument asoriginal_render
function. It can also create amorepath.Response
object directly.