Templates ========= A :term:`template` is a file on disk which can be used to render dynamic data provided by a :term:`view`, usually surrounded by information that is static. :mod:`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. .. _chameleon_zpt_templates: Templating With :term:`Chameleon` ZPT Page Templates ---------------------------------------------------- Like :term:`Zope`, :mod:`repoze.bfg` uses Zope Page Templates (:term:`ZPT`) as its default and best-supported templating language. However, :mod:`repoze.bfg` uses a different implementation of the :term:`ZPT` specification than Zope does: the :term:`Chameleon` 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 :term:`Chameleon` ZPT template named ``foo.pt`` in a directory in your application named ``templates``, you can render the template from a view like so: .. code-block:: python :linenos: from repoze.bfg.chameleon_zpt import render_template_to_response def sample_view(request): return render_template_to_response('templates/foo.pt', foo=1, bar=2) The first argument to :func:`repoze.bfg.chameleon_zpt.render_template_to_response` shown above (and its sister function :func:`repoze.bfg.chameleon_zpt.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 :mod:`repoze.bfg` application's :term:`package` directory. Although a path is usually just a simple relative pathname, a path passed to :func:`repoze.bfg.chameleon_zpt.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 :term:`resource specification` in the form ``some.dotted.package_name:relative/path``, making it possible to address template resources which live in another package. :func:`repoze.bfg.chameleon_zpt.render_template_to_response` always returns a :term:`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 :ref:`chameleon_zpt_module` 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 :func:`repoze.bfg.chameleon_zpt.render_template`: .. code-block:: python :linenos: from repoze.bfg.chameleon_zpt import render_template from webob import Response def sample_view(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 :func:`repoze.bfg.chameleon_zt.render_template_to_response` but changing the content-type and status: .. code-block:: python :linenos: from repoze.bfg.chameleon_zpt import render_template_to_response def sample_view(request): response = render_template_to_response('templates/foo.pt', foo=1, bar=2) response.content_type = 'text/plain' response.status_int = 204 return response Templates Used as Renderers --------------------------- Instead of using the various ``render_template_*`` APIs directly within a view function to render a specific template, you can associate a template (at least one written in a built-in templating language) with a view indirectly by specifying it as a :term:`renderer`. To do so, return a *dictionary* from the view code, and specify the template :term:`resource specification` as the ``renderer`` argument or attribute to the :term:`view configuration` of the view you're trying to render using that template. The items returned by the view in the dictionary will be made available to the template as top-level names. The association of a template as a renderer for a :term:`view configuration` makes it possible to replace code within a :term:`view callable` that handles the rendering of a template. For example, we can replace the call to :func:`repoze.bfg.chameleon_zpt.render_template_to_response` in the below view callable. .. code-block:: python :linenos: from repoze.bfg.chameleon_zpt import render_template_to_response def my_view(request): return render_template_to_response('templates/foo.pt', foo=1, bar=2) Instead, using a :class:`repoze.bfg.view.bfg_view` decorator to specify a :term:`view configuration`, a template renderer for the view can be specified like so: .. code-block:: python :linenos: from repoze.bfg.view import bfg_view @bfg_view(renderer='templates/foo.pt') def my_view(request): return {'foo':1, 'bar':2} Unlike when the various ``render_template_*`` APIs are used, when a template :term:`renderer` is used to render the result of a view callable, several names are passed into the template as top-level names by default, including ``context`` and ``request``. Similar renderer configuration can be done imperatively and via :term:`ZCML`. See :ref:`views_which_use_a_renderer`. See also :ref:`built_in_renderers`. A Sample Template ----------------- Here's what a simple :term:`Chameleon` ZPT template used under :mod:`repoze.bfg` might look like: .. code-block:: xml :linenos: ${project} Application

Welcome to ${project}, an application generated by the repoze.bfg web application framework.

Note the use of :term:`Genshi` -style ``${replacements}`` above. This is one of the ways that :term:`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 :func:`repoze.bfg.chameleon_zpt.render_template` or :func:`repoze.bfg.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 :mod:`repoze.bfg` ------------------------------------- Unlike Zope "browser views", :mod:`repoze.bfg` doesn't make any names such as ``context`` or ``view`` available to :term:`Chameleon` ZPT templates by default unless a :term:`renderer` is used. 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 :mod:`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 :func:`repoze.bfg.chameleon_zpt.get_template` API, and pass it in to the template being rendered. For example, using a :term:`view configuration` via a :class:`repoze.bfg.view.bfg_view` decorator that uses a :term:`renderer`: .. code-block:: python :linenos: from repoze.bfg.chameleon_zpt import get_template from repoze.bfg.view import bfg_view @bfg_view(renderer='templates/mytemplate.pt') def my_view(request): main = get_template('templates/master.pt') return {'main':main} Where ``templates/master.pt`` might look like so: .. code-block:: xml :linenos:

Hello Fred!

And ``templates/mytemplate.pt`` might look like so: .. code-block:: xml :linenos: Chris .. _chameleon_text_templates: Templating with :term:`Chameleon` Text Templates ------------------------------------------------ :mod:`repoze.bfg` also allows for the use of templates which are composed entirely of non-XML text via :term:`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: .. code-block:: python :linenos: from repoze.bfg.chameleon_text import render_template_to_response def text_view(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 :ref:`chameleon_text_module` for the API description. A Chameleon text template can also be used as a :term:`renderer`. See :ref:`built_in_renderers` for more information. 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 ``.cache``. When using ``chameleon.core`` version 1.0b33 and higher or the ``Chameleon`` (uppercase-C) package, this filename is ``.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): .. code-block:: bash :linenos: *.cache *.pt.py .. _reload_templates_section: 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. :mod:`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`` operating system 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 :term:`Chameleon` Template Internationalization ----------------------------------------------- See `the internationalization chapter `_ of the Chameleon documentation for information about supporting internationalized units of text within :term:`Chameleon` templates. Templating with other Templating Languages ------------------------------------------ Because :term:`view callable` functions are typically the only code in :mod:`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 :mod:`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 :term:`WebOb` :term:`Response` object. Assuming you have `Mako `_ installed, here's an example of using Mako from within a :mod:`repoze.bfg` :term:`view`: .. code-block:: python :linenos: from mako.template import Template from webob import Response def make_view(request): template = Template(filename='/templates/template.mak') result = template.render(name=request.params['name']) response = Response(result) return response .. note:: It's reasonably easy to write custom templating system binding packages for use under :mod:`repoze.bfg`. See :ref:`available_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 :ref:`reload_templates_section` will not be available, nor will the template resource overriding capability explained in :ref:`overriding_resources_section` be available, nor will it be possible to use any template using that language as a :term:`renderer`. .. _available_template_system_bindings: Available Add-On Template System Bindings ----------------------------------------- :mod:`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 `_. :mod:`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 :mod:`repoze.bfg` in the :mod:`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 :mod:`zope.pagetemplate` package ("old TAL") are available from `http://svn.repoze.org/repoze.bfg.zopepagetemplate/ `_.