Using a Before Render Event to Expose an h
Helper Object¶
Pylons 1.X exposed a module conventionally named helpers.py
as an h
object in the top-level namespace of each Mako/Genshi/Jinja2 template which
it rendered. You can emulate the same behavior in Pyramid by using a
BeforeRender
event subscriber.
First, create a module named helpers.py
in your Pyramid package at the
top level (next to __init__.py
). We'll import the Python standard
library string
module to use later in a template:
# helpers.py
import string
In the top of the main __init__
module of your Pyramid application
package, import the new helpers
module you created, as well as the
BeforeRender
event type. Underneath the imports create a function that
will act as an event subscriber:
1# __init__.py
2
3from pyramid.events import BeforeRender
4from myapp import helpers
5
6def add_renderer_globals(event):
7 event['h'] = helpers
Within the main
function in the same __init__
, wire the subscriber up
so that it is called when the BeforeRender
event is emitted:
1def main(global_settings, **settings):
2 config = Configurator(....) # existing code
3 # .. existing config statements ... #
4 config.add_subscriber(add_renderer_globals, BeforeRender)
5 # .. other existing config statements and eventual config.make_app()
At this point, with in any view that uses any templating system as a Pyramid
renderer, you will have an omnipresent h
top-level name that is a
reference to the helpers
module you created. For example, if you have a
view like this:
@view_config(renderer='foo.pt')
def aview(request):
return {}
In the foo.pt
Chameleon template, you can do this:
1 ${h.string.uppercase}
The value inserted into the template as the result of this statement will be
ABCDEFGHIJKLMNOPQRSTUVWXYZ
(at least if you are using an English system).
You can add more imports and functions to helpers.py
as necessary to make
features available in your templates.
Using a BeforeRender Event to Expose a Mako base
Template¶
If you wanted to change templates using %inherit
based on if a user was
logged in you could do the following:
1@subscriber(BeforeRender)
2def add_base_template(event):
3 request = event.get('request')
4 if request.user:
5 base = 'myapp:templates/logged_in_layout.mako'
6 event.update({'base': base})
7 else:
8 base = 'myapp:templates/layout.mako'
9 event.update({'base': base})
And then in your mako file you can call %inherit like so:
<%inherit file="${context['base']}" />
You must call the variable this way because of the way Mako works.
It will not know about any other variable other than context
until after
%inherit
is called. Be aware that context
here is not the Pyramid
context in the traversal sense (which is stored in request.context
) but
rather the Mako rendering context.
Using a BeforeRender Event to Expose Chameleon base
Template¶
To avoid defining the same basic things in each template in your application,
you can define one base
template, and inherit from it in other templates.
注釈
Pyramid example application - shootout using this approach.
First, add subscriber within your Pyramid project's __init__.py:
config.add_subscriber('YOURPROJECT.subscribers.add_base_template',
'pyramid.events.BeforeRender')
Then add the subscribers.py
module to your project's directory:
1from pyramid.renderers import get_renderer
2
3def add_base_template(event):
4 base = get_renderer('templates/base.pt').implementation()
5 event.update({'base': base})
After this has been done, you can use your base
template to extend other
templates. For example, the base
template looks like this:
1<html xmlns="http://www.w3.org/1999/xhtml"
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 metal:define-macro="base">
5 <head>
6 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7 <title>My page</title>
8 </head>
9 <body>
10 <tal:block metal:define-slot="content">
11 </tal:block>
12 </body>
13</html>
Each template using the base
template will look like this:
1<html xmlns="http://www.w3.org/1999/xhtml"
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 metal:use-macro="base.macros['base']">
5 <tal:block metal:fill-slot="content">
6 My awesome content.
7 </tal:block>
8</html>
The metal:use-macro="base.macros['base']"
statement is essential here.
Content inside <tal:block metal:fill-slot="content"></tal:block>
tags
will replace corresponding block in base
template. You can define
as many slots in as you want. For more information please see
Macro Expansion Template Attribute Language
documentation.
Using Building Blocks with Chameleon¶
If you understood the base
template chapter, using building blocks
is very simple and straight forward. In the subscribers.py
module
extend the add_base_template
function like this:
1from pyramid.events import subscriber
2from pyramid.events import BeforeRender
3from pyramid.renderers import get_renderer
4
5@subscriber(BeforeRender)
6def add_base_template(event):
7 base = get_renderer('templates/base.pt').implementation()
8 blocks = get_renderer('templates/blocks.pt').implementation()
9 event.update({'base': base,
10 'blocks': blocks,
11 })
Make Pyramid scan the module so that it finds the BeforeRender
event:
1def main(global_settings, **settings):
2 config = Configurator(....) # existing code
3 # .. existing config statements ... #
4 config.scan('subscriber')
5 # .. other existing config statements and eventual config.make_app()
Now, define your building blocks in templates/blocks.pt
. For
example:
1<html xmlns="http://www.w3.org/1999/xhtml"
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal">
4 <tal:block metal:define-macro="base-paragraph">
5 <p class="foo bar">
6 <tal:block metal:define-slot="body">
7 </tal:block>
8 </p>
9 </tal:block>
10
11 <tal:block metal:define-macro="bold-paragraph"
12 metal:extend-macro="macros['base-paragraph']">
13 <tal:block metal:fill-slot="body">
14 <b class="strong-class">
15 <tal:block metal:define-slot="body"></tal:block>
16 </b>
17 </tal:block>
18 </tal:block>
19</html>
You can now use these building blocks like this:
1<html xmlns="http://www.w3.org/1999/xhtml"
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 metal:use-macro="base.macros['base']">
5 <tal:block metal:fill-slot="content">
6 <tal:block metal:use-macro="blocks.macros['base-paragraph']">
7 <tal:block metal:fill-slot="body">
8 My awesome paragraph.
9 </tal:block>
10 </tal:block>
11
12 <tal:block metal:use-macro="blocks.macros['bold-paragraph']">
13 <tal:block metal:fill-slot="body">
14 My awesome paragraph in bold.
15 </tal:block>
16 </tal:block>
17
18 </tal:block>
19</html>
Rendering None
as the Empty String in Mako Templates¶
For the following Mako template:
<p>${nunn}</p>
By default, Pyramid will render:
<p>None</p>
Some folks prefer the value None
to be rendered as the empty string
in a Mako template. In other words, they'd rather the output be:
<p></p>
Use the following settings in your Pyramid configuration file to obtain this behavior:
[app:myapp]
mako.imports = from markupsafe import escape_silent
mako.default_filters = escape_silent