pylons.templating
Covered: 160 lines
Missed: 72 lines
Skipped 102 lines
Percent: 68 %
  1
"""Render functions and helpers
  3
Render functions and helpers
  4
============================
  6
:mod:`pylons.templating` includes several basic render functions,
  7
:func:`render_mako`, :func:`render_genshi` and :func:`render_jinja2`
  8
that render templates from the file-system with the assumption that
  9
variables intended for the will be attached to :data:`tmpl_context`
 10
(hereafter referred to by its short name of :data:`c` which it is
 11
commonly imported as).
 13
The default render functions work with the template language loader
 14
object that is setup on the :data:`app_globals` object in the project's
 15
:file:`config/environment.py`.
 17
Usage
 18
-----
 20
Generally, one of the render functions will be imported in the
 21
controller. Variables intended for the template are attached to the
 22
:data:`c` object. The render functions return unicode (they actually
 23
return :class:`~webhelpers.html.literal` objects, a subclass of
 24
unicode).
 26
.. admonition :: Tip
 28
    :data:`tmpl_context` (template context) is abbreviated to :data:`c`
 29
    instead of its full name since it will likely be used extensively
 30
    and it's much faster to use :data:`c`. Of course, for users that
 31
    can't tolerate one-letter variables, feel free to not import 
 32
    :data:`tmpl_context` as :data:`c` since both names are available in
 33
    templates as well.
 35
Example of rendering a template with some variables::
 37
    from pylons import tmpl_context as c
 38
    from pylons.templating import render_mako as render
 40
    from sampleproject.lib.base import BaseController
 42
    class SampleController(BaseController):
 44
        def index(self):
 45
            c.first_name = "Joe"
 46
            c.last_name = "Smith"
 47
            return render('/some/template.mako')
 49
And the accompanying Mako template:
 51
.. code-block:: mako
 53
    Hello ${c.first name}, I see your lastname is ${c.last_name}!
 55
Your controller will have additional default imports for commonly used
 56
functions.
 58
Template Globals
 59
----------------
 61
Templates rendered in Pylons should include the default Pylons globals
 62
as the :func:`render_mako`, :func:`render_genshi` and
 63
:func:`render_jinja2` functions. The full list of Pylons globals that
 64
are included in the template's namespace are:
 66
- :term:`c` -- Template context object
 67
- :term:`tmpl_context` -- Template context object
 68
- :data:`config` -- Pylons :class:`~pylons.configuration.PylonsConfig`
 69
  object (acts as a dict)
 70
- :term:`app_globals` -- Project application globals object
 71
- :term:`h` -- Project helpers module reference
 72
- :data:`request` -- Pylons :class:`~pylons.controllers.util.Request`
 73
  object for this request
 74
- :data:`response` -- Pylons :class:`~pylons.controllers.util.Response`
 75
  object for this request
 76
- :class:`session` -- Pylons session object (unless Sessions are
 77
  removed)
 78
- :class:`url <routes.util.URLGenerator>` -- Routes url generator
 79
  object
 80
- :class:`translator` -- Gettext translator object configured for
 81
  current locale
 82
- :func:`ungettext` -- Unicode capable version of gettext's ngettext
 83
  function (handles plural translations)
 84
- :func:`_` -- Unicode capable gettext translate function
 85
- :func:`N_` -- gettext no-op function to mark a string for
 86
  translation, but doesn't actually translate
 88
Configuring the template language
 89
---------------------------------
 91
The template engine is created in the projects 
 92
``config/environment.py`` and attached to the ``app_globals`` (g)
 93
instance. Configuration options can be directly passed into the
 94
template engine, and are used by the render functions.
 96
.. warning::
 98
    Don't change the variable name on :data:`app_globals` that the
 99
    template loader is attached to if you want to use the render_*
100
    functions that :mod:`pylons.templating` comes with. The render_*
101
    functions look for the template loader to render the template.
103
"""
104
import logging
106
from webhelpers.html import literal
108
import pylons
110
__all__ = ['render_genshi', 'render_jinja2', 'render_mako', 'render_response']
112
PYLONS_VARS = ['c', 'app_globals', 'config', 'h', 'render', 'request',
113
               'session', 'translator', 'ungettext', '_', 'N_']
115
log = logging.getLogger(__name__)
117
def pylons_globals():
118
    """Create and return a dictionary of global Pylons variables
120
    Render functions should call this to retrieve a list of global
121
    Pylons variables that should be included in the global template
122
    namespace if possible.
124
    Pylons variables that are returned in the dictionary:
125
        ``c``, ``h``, ``_``, ``N_``, config, request, response, 
126
        translator, ungettext, ``url``
128
    If SessionMiddleware is being used, ``session`` will also be
129
    available in the template namespace.
131
    """
132
    conf = pylons.config._current_obj()
133
    c = pylons.tmpl_context._current_obj()
134
    app_globals = conf.get('pylons.app_globals')
135
    pylons_vars = dict(
136
        c=c,
137
        tmpl_context=c,
138
        config=conf,
139
        app_globals=app_globals,
140
        h=conf.get('pylons.h'),
141
        request=pylons.request._current_obj(),
142
        response=pylons.response._current_obj(),
143
        url=pylons.url._current_obj(),
144
        translator=pylons.translator._current_obj(),
145
        ungettext=pylons.i18n.ungettext,
146
        _=pylons.i18n._,
147
        N_=pylons.i18n.N_
148
    )
152
    econf = pylons.config['pylons.environ_config']
153
    if 'beaker.session' in pylons.request.environ or \
154
        ('session' in econf and econf['session'] in pylons.request.environ):
155
        pylons_vars['session'] = pylons.session._current_obj()
156
    log.debug("Created render namespace with pylons vars: %s", pylons_vars)
157
    return pylons_vars
160
def cached_template(template_name, render_func, ns_options=(),
161
                    cache_key=None, cache_type=None, cache_expire=None,
162
                    **kwargs):
163
    """Cache and render a template
165
    Cache a template to the namespace ``template_name``, along with a
166
    specific key if provided.
168
    Basic Options
170
    ``template_name``
171
        Name of the template, which is used as the template namespace.
172
    ``render_func``
173
        Function used to generate the template should it no longer be
174
        valid or doesn't exist in the cache.
175
    ``ns_options``
176
        Tuple of strings, that should correspond to keys likely to be
177
        in the ``kwargs`` that should be used to construct the
178
        namespace used for the cache. For example, if the template
179
        language supports the 'fragment' option, the namespace should
180
        include it so that the cached copy for a template is not the
181
        same as the fragment version of it.
183
    Caching options (uses Beaker caching middleware)
185
    ``cache_key``
186
        Key to cache this copy of the template under.
187
    ``cache_type``
188
        Valid options are ``dbm``, ``file``, ``memory``, ``database``,
189
        or ``memcached``.
190
    ``cache_expire``
191
        Time in seconds to cache this template with this ``cache_key``
192
        for. Or use 'never' to designate that the cache should never
193
        expire.
195
    The minimum key required to trigger caching is
196
    ``cache_expire='never'`` which will cache the template forever
197
    seconds with no key.
199
    """
201
    if cache_key is not None or cache_expire is not None or cache_type \
202
        is not None:
204
        if not cache_type:
205
            cache_type = 'dbm'
206
        if not cache_key:
207
            cache_key = 'default'     
208
        if cache_expire == 'never':
209
            cache_expire = None
210
        namespace = template_name
211
        for name in ns_options:
212
            namespace += str(kwargs.get(name))
213
        cache = pylons.cache.get_cache(namespace, type=cache_type)
214
        content = cache.get_value(cache_key, createfunc=render_func, 
215
            expiretime=cache_expire)
216
        return content
217
    else:
218
        return render_func()
221
def render_mako(template_name, extra_vars=None, cache_key=None, 
222
                cache_type=None, cache_expire=None):
223
    """Render a template with Mako
225
    Accepts the cache options ``cache_key``, ``cache_type``, and
226
    ``cache_expire``.
228
    """    
230
    def render_template():
232
        globs = extra_vars or {}
235
        globs.update(pylons_globals())
238
        template = globs['app_globals'].mako_lookup.get_template(template_name)
240
        return literal(template.render_unicode(**globs))
242
    return cached_template(template_name, render_template, cache_key=cache_key,
243
                           cache_type=cache_type, cache_expire=cache_expire)
246
def render_mako_def(template_name, def_name, cache_key=None,
247
                    cache_type=None, cache_expire=None, **kwargs):
248
    """Render a def block within a Mako template
250
    Takes the template name, and the name of the def within it to call.
251
    If the def takes arguments, they should be passed in as keyword
252
    arguments.
254
    Example::
258
        render_mako_def('layout.mako', 'header', title='Testing')
260
    Also accepts the cache options ``cache_key``, ``cache_type``, and
261
    ``cache_expire``.
263
    """
265
    def render_template():
267
        globs = kwargs or {}
270
        globs.update(pylons_globals())
273
        template = globs['app_globals'].mako_lookup.get_template(
274
            template_name).get_def(def_name)
276
        return literal(template.render_unicode(**globs))
278
    return cached_template(template_name, render_template, cache_key=cache_key,
279
                           cache_type=cache_type, cache_expire=cache_expire)
282
def render_genshi(template_name, extra_vars=None, cache_key=None, 
283
                  cache_type=None, cache_expire=None, method='xhtml'):
284
    """Render a template with Genshi
286
    Accepts the cache options ``cache_key``, ``cache_type``, and
287
    ``cache_expire`` in addition to method which are passed to Genshi's
288
    render function.
290
    """
292
    def render_template():
294
        globs = extra_vars or {}
297
        globs.update(pylons_globals())
300
        template = globs['app_globals'].genshi_loader.load(template_name)
302
        return literal(template.generate(**globs).render(method=method,
303
                                                         encoding=None))
305
    return cached_template(template_name, render_template, cache_key=cache_key,
306
                           cache_type=cache_type, cache_expire=cache_expire,
307
                           ns_options=('method'), method=method)
310
def render_jinja2(template_name, extra_vars=None, cache_key=None, 
311
                 cache_type=None, cache_expire=None):
312
    """Render a template with Jinja2
314
    Accepts the cache options ``cache_key``, ``cache_type``, and
315
    ``cache_expire``.
317
    """    
319
    def render_template():
321
        globs = extra_vars or {}
324
        globs.update(pylons_globals())
327
        template = \
328
            globs['app_globals'].jinja2_env.get_template(template_name)
330
        return literal(template.render(**globs))
332
    return cached_template(template_name, render_template, cache_key=cache_key,
333
                           cache_type=cache_type, cache_expire=cache_expire)