diff options
| author | Chris McDonough <chrism@plope.com> | 2010-12-19 18:04:55 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-12-19 18:04:55 -0500 |
| commit | a59c789b2bb4a1de5f79537cc2f1022c97bb51f6 (patch) | |
| tree | b5052ade4fc0718c9d2ff05b4a51e6f59f85f912 /docs/narr/renderers.rst | |
| parent | e8f14759a3cf00b78735ef7e7ae8949f68b29b2b (diff) | |
| download | pyramid-a59c789b2bb4a1de5f79537cc2f1022c97bb51f6.tar.gz pyramid-a59c789b2bb4a1de5f79537cc2f1022c97bb51f6.tar.bz2 pyramid-a59c789b2bb4a1de5f79537cc2f1022c97bb51f6.zip | |
- Split off "Renderers" as its own chapter from "Views" chapter in narrative
documentation.
Diffstat (limited to 'docs/narr/renderers.rst')
| -rw-r--r-- | docs/narr/renderers.rst | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst new file mode 100644 index 000000000..3804fcf42 --- /dev/null +++ b/docs/narr/renderers.rst @@ -0,0 +1,594 @@ +.. _renderers_chapter: + +Renderers +========= + +In the :ref:`views_chapter` chapter, we said that a view callable must +return a :term:`Response` object. We lied. A :term:`renderer` is a service +that attempts to convert a non-Response return value of a function, class, or +instance that acts as a :term:`view callable` to a :term:`Response` object. + +Overview +-------- + +A view needn't *always* return a Response object. If a view happens to +return something which does not implement the Pyramid Response interface, +:app:`Pyramid` will attempt to use a :term:`renderer` to construct a +response. For example: + +.. code-block:: python + :linenos: + + from pyramid.response import Response + from pyramid.view import view_config + + def hello_world(request): + return {'content':'Hello!'} + +The above example returns a *dictionary* from the view callable. A +dictionary does not implement the Pyramid response interface, so you might +believe that this example would fail. However, since a ``renderer`` is +associated with the view callable through its :term:`view configuration` (in +this case, using a ``renderer`` argument passed to +:func:`pyramid.view.view_config`), if the view does *not* return a Response +object, the renderer will attempt to convert the result of the view to a +response on the developer's behalf. + +Of course, if no renderer is associated with a view's configuration, +returning anything except an object which implements the Response interface +will result in an error. And, if a renderer *is* used, whatever is returned +by the view must be compatible with the particular kind of renderer used, or +an error may occur during view invocation. + +One exception exists: it is *always* OK to return a Response object, even +when a ``renderer`` is configured. If a view callable returns a response +object from a view that is configured with a renderer, the renderer is +bypassed entirely. + +Various types of renderers exist, including serialization renderers +and renderers which use templating systems. See also +:ref:`views_which_use_a_renderer`. + + +.. index:: + single: renderer + single: view renderer + +.. _views_which_use_a_renderer: + +Writing View Callables Which Use a Renderer +------------------------------------------- + +As we've seen, view callables needn't always return a Response object. +Instead, they may return an arbitrary Python object, with the expectation +that a :term:`renderer` will convert that object into a response instance on +your behalf. Some renderers use a templating system; other renderers use +object serialization techniques. + +View configuration can vary the renderer associated with a view callable via +the ``renderer`` attribute. For example, this call to +:meth:`pyramid.config.Configurator.add_view` associates the ``json`` renderer +with a view callable: + +.. code-block:: python + :linenos: + + config.add_view('myproject.views.my_view', renderer='json') + +When this configuration is added to an application, the +``myproject.views.my_view`` view callable will now use a ``json`` renderer, +which renders view return values to a :term:`JSON` response serialization. + +Other built-in renderers include renderers which use the :term:`Chameleon` +templating language to render a dictionary to a response. + +If the :term:`view callable` associated with a :term:`view configuration` +returns a Response object directly (an object with the attributes ``status``, +``headerlist`` and ``app_iter``), any renderer associated with the view +configuration is ignored, and the response is passed back to :app:`Pyramid` +unmolested. For example, if your view callable returns an instance of the +:class:`pyramid.httpexceptions.HTTPFound` class as a response, no renderer +will be employed. + +.. code-block:: python + :linenos: + + from pyramid.httpexceptions import HTTPFound + + def view(request): + return HTTPFound(location='http://example.com') # any renderer avoided + +Views which use a renderer can vary non-body response attributes (such as +headers and the HTTP status code) by attaching properties to the request. +See :ref:`response_request_attrs`. + +Additional renderers can be added by developers to the system as necessary +(see :ref:`adding_and_overriding_renderers`). + +.. index:: + single: renderers (built-in) + single: built-in renderers + +.. _built_in_renderers: + +Built-In Renderers +------------------ + +Several built-in renderers exist in :app:`Pyramid`. These renderers can be +used in the ``renderer`` attribute of view configurations. + +.. index:: + pair: renderer; string + +``string``: String Renderer +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``string`` renderer is a renderer which renders a view callable result to +a string. If a view callable returns a non-Response object, and the +``string`` renderer is associated in that view's configuration, the result +will be to run the object through the Python ``str`` function to generate a +string. Note that if a Unicode object is returned by the view callable, it +is not ``str()`` -ified. + +Here's an example of a view that returns a dictionary. If the ``string`` +renderer is specified in the configuration for this view, the view will +render the returned dictionary to the ``str()`` representation of the +dictionary: + +.. code-block:: python + :linenos: + + from pyramid.response import Response + from pyramid.view import view_config + + @view_config(renderer='string') + def hello_world(request): + return {'content':'Hello!'} + +The body of the response returned by such a view will be a string +representing the ``str()`` serialization of the return value: + +.. code-block:: python + :linenos: + + {'content': 'Hello!'} + +Views which use the string renderer can vary non-body response attributes by +attaching properties to the request. See :ref:`response_request_attrs`. + +.. index:: + pair: renderer; JSON + +``json``: JSON Renderer +~~~~~~~~~~~~~~~~~~~~~~~ + +The ``json`` renderer renders view callable results to :term:`JSON`. It +passes the return value through the ``json.dumps`` standard library function, +and wraps the result in a response object. It also sets the response +content-type to ``application/json``. + +Here's an example of a view that returns a dictionary. Since the ``json`` +renderer is specified in the configuration for this view, the view will +render the returned dictionary to a JSON serialization: + +.. code-block:: python + :linenos: + + from pyramid.response import Response + from pyramid.view import view_config + + @view_config(renderer='json') + def hello_world(request): + return {'content':'Hello!'} + +The body of the response returned by such a view will be a string +representing the JSON serialization of the return value: + +.. code-block:: python + :linenos: + + '{"content": "Hello!"}' + +The return value needn't be a dictionary, but the return value must contain +values serializable by :func:`json.dumps`. + +You can configure a view to use the JSON renderer by naming ``json`` as the +``renderer`` argument of a view configuration, e.g. by using +:meth:`pyramid.config.Configurator.add_view`: + +.. code-block:: python + :linenos: + + config.add_view('myproject.views.hello_world', + name='hello', + context='myproject.resources.Hello', + renderer='json') + + +Views which use the JSON renderer can vary non-body response attributes by +attaching properties to the request. See :ref:`response_request_attrs`. + +.. index:: + pair: renderer; chameleon + +.. _chameleon_template_renderers: + +``*.pt`` or ``*.txt``: Chameleon Template Renderers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Two built-in renderers exist for :term:`Chameleon` templates. + +If the ``renderer`` attribute of a view configuration is an absolute path, a +relative path or :term:`asset specification` which has a final path element +with a filename extension of ``.pt``, the Chameleon ZPT renderer is used. +See :ref:`chameleon_zpt_templates` for more information about ZPT templates. + +If the ``renderer`` attribute of a view configuration is an absolute path or +a :term:`asset specification` which has a final path element with a filename +extension of ``.txt``, the :term:`Chameleon` text renderer is used. See +:ref:`chameleon_zpt_templates` for more information about Chameleon text +templates. + +The behavior of these renderers is the same, except for the engine +used to render the template. + +When a ``renderer`` attribute that names a template path or :term:`asset +specification` (e.g. ``myproject:templates/foo.pt`` or +``myproject:templates/foo.txt``) is used, the view must return a +:term:`Response` object or a Python *dictionary*. If the view callable with +an associated template returns a Python dictionary, the named template will +be passed the dictionary as its keyword arguments, and the template renderer +implementation will return the resulting rendered template in a response to +the user. If the view callable returns anything but a Response object or a +dictionary, an error will be raised. + +Before passing keywords to the template, the keyword arguments derived from +the dictionary returned by the view are augmented. The callable object -- +whatever object was used to define the ``view`` -- will be automatically +inserted into the set of keyword arguments passed to the template as the +``view`` keyword. If the view callable was a class, the ``view`` keyword +will be an instance of that class. Also inserted into the keywords passed to +the template are ``renderer_name`` (the string used in the ``renderer`` +attribute of the directive), ``renderer_info`` (an object containing +renderer-related information), ``context`` (the context resource of the view +used to render the template), and ``request`` (the request passed to the view +used to render the template). + +Here's an example view configuration which uses a Chameleon ZPT renderer: + +.. code-block:: python + :linenos: + + # config is an instance of pyramid.config.Configurator + + config.add_view('myproject.views.hello_world', + name='hello', + context='myproject.resources.Hello', + renderer='myproject:templates/foo.pt') + +Here's an example view configuration which uses a Chameleon text renderer: + +.. code-block:: python + :linenos: + + config.add_view('myproject.views.hello_world', + name='hello', + context='myproject.resources.Hello', + renderer='myproject:templates/foo.txt') + +Views which use a Chameleon renderer can vary response attributes by +attaching properties to the request. See :ref:`response_request_attrs`. + +.. index:: + pair: renderer; mako + +.. _mako_template_renderers: + +``*.mak`` or ``*.mako``: Mako Template Renderer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Mako`` template renderer renders views using a Mako template. When +used, the view must return a Response object or a Python *dictionary*. The +dictionary items will then be used in the global template space. If the view +callable returns anything but a Response object, or a dictionary, an error +will be raised. + +When using a ``renderer`` argument to a :term:`view configuration` to specify +a Mako template, the value of the ``renderer`` may be a path relative to the +``mako.directories`` setting (e.g. ``some/template.mak``) or, alternately, +it may be a :term:`asset specification` +(e.g. ``apackage:templates/sometemplate.mak``). Mako templates may +internally inherit other Mako templates using a relative filename or a +:term:`asset specification` as desired. + +Here's an example view configuration which uses a relative path: + +.. code-block:: python + :linenos: + + # config is an instance of pyramid.config.Configurator + + config.add_view('myproject.views.hello_world', + name='hello', + context='myproject.resources.Hello', + renderer='foo.mak') + +It's important to note that in Mako's case, the 'relative' path name +``foo.mak`` above is not relative to the package, but is relative to the +directory (or directories) configured for Mako via the ``mako.directories`` +configuration file setting. + +The renderer can also be provided in :term:`asset specification` +format. Here's an example view configuration which uses one: + +.. code-block:: python + :linenos: + + config.add_view('myproject.views.hello_world', + name='hello', + context='myproject.resources.Hello', + renderer='mypackage:templates/foo.mak') + +The above configuration will use the file named ``foo.mak`` in the +``templates`` directory of the ``mypackage`` package. + +The ``Mako`` template renderer can take additional arguments beyond the +standard ``reload_templates`` setting, see the :ref:`environment_chapter` for +additional :ref:`mako_template_renderer_settings`. + +.. index:: + single: response headers (from a renderer) + single: renderer response headers + +.. _response_request_attrs: + +Varying Attributes of Rendered Responses +---------------------------------------- + +Before a response constructed by a :term:`renderer` is returned to +:app:`Pyramid`, several attributes of the request are examined which have the +potential to influence response behavior. + +View callables that don't directly return a response should set these +attributes on the ``request`` object via ``setattr`` during their execution, +to influence associated response attributes. + +``response_content_type`` + Defines the content-type of the resulting response, + e.g. ``text/xml``. + +``response_headerlist`` + A sequence of tuples describing cookie values that should be set in the + response, e.g. ``[('Set-Cookie', 'abc=123'), ('X-My-Header', 'foo')]``. + +``response_status`` + A WSGI-style status code (e.g. ``200 OK``) describing the status of the + response. + +``response_charset`` + The character set (e.g. ``UTF-8``) of the response. + +``response_cache_for`` + A value in seconds which will influence ``Cache-Control`` and ``Expires`` + headers in the returned response. The same can also be achieved by + returning various values in the ``response_headerlist``, this is purely a + convenience. + +For example, if you need to change the response status from within a view +callable that uses a renderer, assign the ``response_status`` attribute to +the request before returning a result: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + + @view_config(name='gone', renderer='templates/gone.pt') + def myview(request): + request.response_status = '404 Not Found' + return {'URL':request.URL} + +For more information on attributes of the request, see the API +documentation in :ref:`request_module`. + +.. index:: + single: renderer (adding) + +.. _adding_and_overriding_renderers: + +Adding and Overriding Renderers +------------------------------- + +New templating systems and serializers can be associated with :app:`Pyramid` +renderer names. To this end, configuration declarations can be made which +override an existing :term:`renderer factory`, and which add a new renderer +factory. + +Renderers can be registered imperatively using the +:meth:`pyramid.config.Configurator.add_renderer` API. + +.. note:: The tasks described in this section can also be performed via + :term:`declarative configuration`. See + :ref:`zcml_adding_and_overriding_renderers`. + +For example, to add a renderer which renders views which have a +``renderer`` attribute that is a path that ends in ``.jinja2``: + +.. code-block:: python + :linenos: + + config.add_renderer('.jinja2', 'mypackage.MyJinja2Renderer') + +The first argument is the renderer name. The second argument is a reference +to an implementation of a :term:`renderer factory` or a :term:`dotted Python +name` referring to such an object. + +.. _adding_a_renderer: + +Adding a New Renderer +~~~~~~~~~~~~~~~~~~~~~ + +You may add a new renderer by creating and registering a :term:`renderer +factory`. + +A renderer factory implementation is typically a class with the +following interface: + +.. code-block:: python + :linenos: + + class RendererFactory: + def __init__(self, info): + """ Constructor: ``info`` will be an object having the + the following attributes: ``name`` (the renderer name), ``package`` + (the package that was 'current' at the time the renderer was + registered), ``type`` (the renderer type name), ``registry`` + (the current application registry) and ``settings`` (the + deployment settings dictionary). + """ + + def __call__(self, value, system): + """ Call a the renderer implementation with the value and + the system value passed in as arguments and return the + result (a string or unicode object). The value is the + return value of a view. The system value is a dictionary + containing available system values (e.g. ``view``, + ``context``, and ``request``). """ + +The formal interface definition of the ``info`` object passed to a renderer +factory constructor is available as :class:`pyramid.interfaces.IRendererInfo`. + +There are essentially two different kinds of renderer factories: + +- A renderer factory which expects to accept a :term:`asset + specification`, or an absolute path, as the ``name`` attribute of the + ``info`` object fed to its constructor. These renderer factories are + registered with a ``name`` value that begins with a dot (``.``). These + types of renderer factories usually relate to a file on the filesystem, + such as a template. + +- A renderer factory which expects to accept a token that does not represent + a filesystem path or a asset specification in the ``name`` + attribute of the ``info`` object fed to its constructor. These renderer + factories are registered with a ``name`` value that does not begin with a + dot. These renderer factories are typically object serializers. + +.. sidebar:: Asset Specifications + + A asset specification is a colon-delimited identifier for a + :term:`asset`. The colon separates a Python :term:`package` + name from a package subpath. For example, the asset + specification ``my.package:static/baz.css`` identifies the file named + ``baz.css`` in the ``static`` subdirectory of the ``my.package`` Python + :term:`package`. + +Here's an example of the registration of a simple renderer factory via +:meth:`pyramid.config.Configurator.add_renderer`: + +.. code-block:: python + :linenos: + + # config is an instance of pyramid.config.Configurator + + config.add_renderer(name='amf', factory='my.package.MyAMFRenderer') + +Adding the above code to your application startup configuration will +allow you to use the ``my.package.MyAMFRenderer`` renderer factory +implementation in view configurations. Your application can use this +renderer by specifying ``amf`` in the ``renderer`` attribute of a +:term:`view configuration`: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + + @view_config(renderer='amf') + def myview(request): + return {'Hello':'world'} + +At startup time, when a :term:`view configuration` is encountered, which +has a ``name`` attribute that does not contain a dot, the full ``name`` +value is used to construct a renderer from the associated renderer +factory. In this case, the view configuration will create an instance +of an ``AMFRenderer`` for each view configuration which includes ``amf`` +as its renderer value. The ``name`` passed to the ``AMFRenderer`` +constructor will always be ``amf``. + +Here's an example of the registration of a more complicated renderer +factory, which expects to be passed a filesystem path: + +.. code-block:: python + :linenos: + + config.add_renderer(name='.jinja2', + factory='my.package.MyJinja2Renderer') + +Adding the above code to your application startup will allow you to use the +``my.package.MyJinja2Renderer`` renderer factory implementation in view +configurations by referring to any ``renderer`` which *ends in* ``.jinja`` in +the ``renderer`` attribute of a :term:`view configuration`: + +.. code-block:: python + :linenos: + + from pyramid.view import view_config + + @view_config(renderer='templates/mytemplate.jinja2') + def myview(request): + return {'Hello':'world'} + +When a :term:`view configuration` is encountered at startup time, which +has a ``name`` attribute that does contain a dot, the value of the name +attribute is split on its final dot. The second element of the split is +typically the filename extension. This extension is used to look up a +renderer factory for the configured view. Then the value of +``renderer`` is passed to the factory to create a renderer for the view. +In this case, the view configuration will create an instance of a +``Jinja2Renderer`` for each view configuration which includes anything +ending with ``.jinja2`` in its ``renderer`` value. The ``name`` passed +to the ``Jinja2Renderer`` constructor will be the full value that was +set as ``renderer=`` in the view configuration. + +See also :ref:`renderer_directive` and +:meth:`pyramid.config.Configurator.add_renderer`. + +Overriding an Existing Renderer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can associate more than one filename extension with the same existing +renderer implementation as necessary if you need to use a different file +extension for the same kinds of templates. For example, to associate the +``.zpt`` extension with the Chameleon ZPT renderer factory, use the +:meth:`pyramid.config.Configurator.add_renderer` method: + +.. code-block:: python + :linenos: + + config.add_renderer('.zpt', 'pyramid.chameleon_zpt.renderer_factory') + +After you do this, :app:`Pyramid` will treat templates ending in both the +``.pt`` and ``.zpt`` filename extensions as Chameleon ZPT templates. + +To override the default mapping in which files with a ``.pt`` extension are +rendered via a Chameleon ZPT page template renderer, use a variation on the +following in your application's startup code: + +.. code-block:: python + :linenos: + + config.add_renderer('.pt', 'mypackage.pt_renderer') + +After you do this, the :term:`renderer factory` in +``mypackage.pt_renderer`` will be used to render templates which end +in ``.pt``, replacing the default Chameleon ZPT renderer. + +To associate a *default* renderer with *all* view configurations (even +ones which do not possess a ``renderer`` attribute), pass ``None`` as +the ``name`` attribute to the renderer tag: + +.. code-block:: python + :linenos: + + config.add_renderer(None, 'mypackage.json_renderer_factory') + |
