diff options
Diffstat (limited to 'docs/narr/templates.rst')
| -rw-r--r-- | docs/narr/templates.rst | 440 |
1 files changed, 266 insertions, 174 deletions
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index f6f4098e0..4f5124a59 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -2,134 +2,277 @@ 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. +dynamic data provided by a :term:`view`. :mod:`repoze.bfg` offers a +number of ways to perform templating tasks out of the box, and +provides add-on templating support through a set of bindings packages. -.. index:: - triple: Chameleon; ZPT; templates +Out of the box, :mod:`repoze.bfg` provides templating via the +:term:`Chameleon` templating library. :term:`Chameleon` provides +support for two different types of templates: :term:`ZPT` templates +and text templates. -.. _chameleon_zpt_templates: +Before discussing how built-in templates are templates are used in +detail, we'll discuss two ways to render templates within +:mod:`repoze.bfg` in general: directly, and via renderer +configuration. -Templating With :term:`Chameleon` ZPT Page Templates ----------------------------------------------------- +.. index:: + single: templates used directly + single: Mako -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 <http://wiki.zope.org/ZPT/FrontPage>`_ template -specification, however it is significantly faster. +.. _templates_used_directly: -.. note:: The language definition documentation for Chameleon - ZPT-style templates is available from `the Chameleon website - <http://chameleon.repoze.org>`_. See its `documentation - <http://chameleon.repoze.org/docs/latest/>`_ for the Chameleon ZPT - language specification. +Templates Used Directly +----------------------- -Given that there is a :term:`Chameleon` ZPT template named ``foo.pt`` +The most straightforward way to use a template within +:mod:`repoze.bfg` is to cause it to be rendered directly within a +:term:`view callable`. You may use whatever API is supplied by a +given templating engine to do so. + +:mod:`repoze.bfg` provides various APIs that allow you to render +:term:`Chameleon` templates directly from within a view callable. For +example, if 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: +the template from within the body of view callable 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`: +The ``sample_view`` :term:`view callable` above returns a +:term:`response` object which contains the body of the +``template/foo.pt`` template. The template author will have the names +``foo`` and ``bar`` available as top-level names for replacement or +comparison purposes. + +Every views must return a :term:`response` object (except for views +which use a :term:`renderer`, which we'll see shortly). The +:func:`repoze.bfg.chameleon_zpt.render_template_to_response` function +is a shortcut function that actually returns a response object, but +not all template APIs know about responses. When you use an template +API that is "response-ignorant" you can also easily render a template +to a string, and construct your own response object as necessary with +the string as the body. + +For example, the :func:`repoze.bfg.chameleon_zpt.render_template` API +returns a string. We can manufacture a :term:`response` object +directly, and use that string as the body of the response: .. 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_zpt.render_template_to_response` but -changing the content-type and status: +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. + +For example, here's an example of using `Mako +<http://www.makotemplates.org/>`_ from within a :mod:`repoze.bfg` +:term:`view`: + +.. ignore-next-block +.. 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:: + + If you use third-party templating languages without cooperating BFG + bindings directly within view callables, 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`. However, it's reasonably easy to write custom + templating system binding packages for use under :mod:`repoze.bfg` + so that templates written in the language can be used as renderers. + See :ref:`available_template_system_bindings` for example packages. + +If you need more control over the status code and content-type, or +other response attributes from views that use direct templating, you +may set attributes on the response that influence these values. + +Here's an example of changing the content-type and status of the +response object returned by +:func:`repoze.bfg.chameleon_zpt.render_template_to_response`: .. 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 +Here's an example of manufacturing a response object using the result of +:func:`repoze.bfg.chameleon_zpt.render_template` (a string): + +.. 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 + .. index:: single: templates used as renderers pair: renderers; template +.. _templates_used_as_renderers: + 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 +Instead of using templating system APIs within a the body of a view +function directly to render a specific template, you may associate a +template written in a supported templating language with a view +indirectly by specifying it as a :term:`renderer`. + +To use a renderer, specify a template :term:`resource specification` +as the ``renderer`` argument or attribute to the :term:`view +configuration` of a :term:`view callable`. Then return a *dictionary* +from that view callable. The dictionary items returned by the view +callable will be made available to the renderer 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. +callable` that handles the rendering of a template. + +Here's an example of using a :class:`repoze.bfg.view.bfg_view` +decorator to specify a :term:`view configuration` that names a +template renderer: .. code-block:: python :linenos: - from repoze.bfg.chameleon_zpt import render_template_to_response + from repoze.bfg.view import bfg_view + + @bfg_view(renderer='templates/foo.pt') def my_view(request): - return render_template_to_response('templates/foo.pt', foo=1, bar=2) + return {'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: +The ``renderer`` argument to the ``@bfg_view`` configuration decorator +shown above 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 file which defines the view +configuration lives. In this case, this is the directory containing +the file that defines the ``my_view`` function. + +Although a renderer path is usually just a simple relative pathname, a +path named as a renderer 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. + +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`. + +Not just any template from any arbitrary templating systemmay be used +as a renderer. Bindings must exist specifically for :mod:`repoze.bfg` +to use a templating language template as a renderer. Currently, +:mod:`repoze.bfg` has built-in support for two Chameleon templating +languages: ZPT and text. See :ref:`built_in_renderers` for a +discussion of their details. :mod:`repoze.bfg` also supports the use +of :term:`Jinja2` templates as renderers. See +:ref:`available_template_system_bindings`. + +.. sidebar:: Why Use A Renderer + + Using a renderer is usually a better way to render templates than + using any templating API directly from within a :term:`view + callable` because it makes the view callable more unit-testable. + Views which use templating APIs directly must return a + :term:`Response` object. Making testing assertions about response + objects is typically an indirect process, because it means that + your test code often needs to somehow needs to parse information + out of the response body (often HTML). View callables which use + renderers typically return a dictionary, and making assertions + about the information is almost always more direct than needing to + parse HTML. + + Specifying a renderer from within :term:`ZCML` (as opposed to + imperatively or via a ``bfg_view`` decorator, or using a template + directly from within a view callable) also makes it possible for + someone to modify the template used to render a view without + needing to fork your code to do so. See :ref:`extending_chapter` + for more information. + +By default, views rendered via a template renderer return a +:term:`Response` object which has a *status code* of ``200 OK`` and a +*content-type* of ``text/html``. To vary attributes of the response +of a view that uses a renderer, such as the content-type, headers, or +status attributes, you must set attributes on the *request* object +within the view before returning the dictionary. See +:ref:`response_request_attrs` for more information. + +.. index:: + triple: Chameleon; ZPT; templates + +.. _chameleon_zpt_templates: + +:term:`Chameleon` ZPT Templates +------------------------------- + +Like :term:`Zope`, :mod:`repoze.bfg` uses :term:`ZPT` (Zope Page +Templates) as its default templating language. However, +:mod:`repoze.bfg` uses a different implementation of the :term:`ZPT` +specification than Zope does: the :term:`Chameleon` templating +engine. The Chameleon engine complies largely with the `Zope Page +Template <http://wiki.zope.org/ZPT/FrontPage>`_ template +specification. However, it is significantly faster. + +The language definition documentation for Chameleon ZPT-style +templates is available from `the Chameleon website +<http://chameleon.repoze.org/>`_. + +.. warning:: + + :term:`Chameleon` only works on :term:`CPython` platforms. This + does not include Google's App Engine nor :term:`Jython`. On these + platforms, you should use ``repoze.bfg.jinja2`` instead. See + :ref:`available_template_system_bindings`. + +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 as a :term:`renderer` like so: .. code-block:: python :linenos: @@ -140,19 +283,18 @@ can be specified like so: 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`. +If you'd rather use templates directly within a view callable (without +the indirection of using a renderer), see :ref:`chameleon_zpt_module` +for the API description. + +See also :ref:`built_in_renderers` for more general information about +renderers, including Chameleon ZPT renderers. .. index:: single: sample template -A Sample Template ------------------ +A Sample ZPT Template +~~~~~~~~~~~~~~~~~~~~~ Here's what a simple :term:`Chameleon` ZPT template used under :mod:`repoze.bfg` might look like: @@ -189,25 +331,24 @@ works in these templates. single: ZPT macros 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`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a :term:`renderer` is used to render a template, +:mod:`repoze.bfg` makes at least two top-level names available to the +template by default: ``context`` and ``request``. 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``. But having a hold of +the context in :mod:`repoze.bfg` is not helpful: templates cannot +usually be retrieved from models. To use macros 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: @@ -262,8 +403,8 @@ 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:: +on disk named ``mytemplate.txt`` in your project's ``templates`` +directory with the following contents:: Hello, ${name}! @@ -273,17 +414,25 @@ which renders this template: .. code-block:: python :linenos: - from repoze.bfg.chameleon_text import render_template_to_response + from repoze.bfg.chameleon_zpt import get_template + from repoze.bfg.view import bfg_view + + @bfg_view(renderer='templates/mytemplate.txt') + def my_view(request): + return {'name':'world'} + +When the template is renderered, it will show: + +.. code-block:: text - def text_view(request): - return render_template_to_response('templates/text.txt', name='World') + Hello, 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. +If you'd rather use templates directly within a view callable (without +the indirection of using a renderer), 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. +See also :ref:`built_in_renderers` for more general information about +renderers, including Chameleon text renderers. .. index:: pair: template renderer; side effects @@ -294,13 +443,9 @@ 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 or the -``Chameleon`` (uppercase-C) package, 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. +template needs to be read from disk. If you see "strange" ``.py`` +files showing up in your ``templates`` directory (or otherwise +directly "next" to your templates), 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 @@ -311,8 +456,8 @@ should cause it to ignore these files. Here's the contents of my .. code-block:: bash :linenos: - *.cache *.pt.py + *.txt.py .. index:: pair: template; automatic reloading @@ -361,48 +506,6 @@ documentation for information about supporting internationalized units of text within :term:`Chameleon` templates. .. index:: - single: other templating languages - -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 -<http://www.makotemplates.org/>`_ installed, here's an example of -using Mako from within a :mod:`repoze.bfg` :term:`view`: - -.. ignore-next-block -.. code-block:: python - :linenos: - - from mako.template import Template - from webob import Response - - def mako_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`. - -.. index:: single: template system bindings single: Jinja2 single: Genshi @@ -412,19 +515,8 @@ possible to use any template using that language as a 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 -<http://svn.repoze.org/repoze.bfg.xslt>`_. - -:mod:`repoze.bfg.chameleon_genshi` 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 -<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 +``repoze.bfg.jinja2`` package. It lives in the Repoze Subversion repository at `http://svn.repoze.org/repoze.bfg.jinja2 <http://svn.repoze.org/repoze.bfg.jinja2>`_; it is also available from :term:`PyPI`. |
