Permalink
Newer
Older
100644 141 lines (113 sloc) 4.47 KB
1
# -*- coding: utf-8 -*-
2
"""
3
flask.templating
4
~~~~~~~~~~~~~~~~
5
6
Implements the bridge to Jinja2.
7
8
:copyright: (c) 2011 by Armin Ronacher.
9
:license: BSD, see LICENSE for more details.
10
"""
11
import posixpath
12
from jinja2 import BaseLoader, Environment as BaseEnvironment, \
13
TemplateNotFound
15
from .globals import _request_ctx_stack
16
from .signals import template_rendered
17
from .module import blueprint_is_module
20
def _default_template_ctx_processor():
21
"""Default template context processor. Injects `request`,
22
`session` and `g`.
23
"""
24
reqctx = _request_ctx_stack.top
25
return dict(
27
request=reqctx.request,
28
session=reqctx.session,
29
g=reqctx.g
30
)
31
32
33
class Environment(BaseEnvironment):
34
"""Works like a regular Jinja2 environment but has some additional
35
knowledge of how Flask's blueprint works so that it can prepend the
36
name of the blueprint to referenced templates if necessary.
37
"""
38
39
def __init__(self, app, **options):
40
if 'loader' not in options:
41
options['loader'] = app.create_global_jinja_loader()
42
BaseEnvironment.__init__(self, **options)
43
self.app = app
44
45
46
class DispatchingJinjaLoader(BaseLoader):
47
"""A loader that looks for templates in the application and all
49
"""
50
51
def __init__(self, app):
52
self.app = app
53
54
def get_source(self, environment, template):
55
for loader, local_name in self._iter_loaders(template):
56
try:
57
return loader.get_source(environment, local_name)
58
except TemplateNotFound:
59
pass
60
61
raise TemplateNotFound(template)
63
def _iter_loaders(self, template):
64
loader = self.app.jinja_loader
65
if loader is not None:
66
yield loader, template
67
68
# old style module based loaders in case we are dealing with a
69
# blueprint that is an old style module
71
module, local_name = posixpath.normpath(template).split('/', 1)
72
blueprint = self.app.blueprints[module]
73
if blueprint_is_module(blueprint):
74
loader = blueprint.jinja_loader
75
if loader is not None:
76
yield loader, local_name
77
except (ValueError, KeyError):
80
for blueprint in self.app.blueprints.itervalues():
81
if blueprint_is_module(blueprint):
82
continue
83
loader = blueprint.jinja_loader
84
if loader is not None:
85
yield loader, template
86
87
def list_templates(self):
88
result = set()
89
loader = self.app.jinja_loader
90
if loader is not None:
91
result.update(loader.list_templates())
92
93
for name, blueprint in self.app.blueprints.iteritems():
94
loader = blueprint.jinja_loader
95
if loader is not None:
96
for template in loader.list_templates():
97
prefix = ''
98
if blueprint_is_module(blueprint):
99
prefix = name + '/'
100
result.add(prefix + template)
105
def _render(template, context, app):
106
"""Renders the template and fires the signal"""
107
rv = template.render(context)
108
template_rendered.send(app, template=template, context=context)
109
return rv
110
111
112
def render_template(template_name_or_list, **context):
113
"""Renders a template from the template folder with the given
114
context.
115
116
:param template_name_or_list: the name of the template to be
117
rendered, or an iterable with template names
118
the first one existing will be rendered
119
:param context: the variables that should be available in the
120
context of the template.
121
"""
122
ctx = _request_ctx_stack.top
123
ctx.app.update_template_context(context)
124
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
125
context, ctx.app)
126
127
128
def render_template_string(source, **context):
129
"""Renders a template from the given template source string
130
with the given context.
131
132
:param source: the sourcecode of the template to be
133
rendered
134
:param context: the variables that should be available in the
135
context of the template.
136
"""
137
ctx = _request_ctx_stack.top
138
ctx.app.update_template_context(context)
139
return _render(ctx.app.jinja_env.from_string(source),
140
context, ctx.app)