Skip to content

Commit a6e9ad5

Browse files
samuelcolvinasvetlov
authored andcommitted
adding helper filters url and static (aio-libs#97)
* adding helper filters url and static * switching to contextfunction * rename test_jinja_globals.py & add tests * adding docs for url and static functions * full coverage to please codecov * @asvetlov's comments on aio-libs#130 * adapting as per comments
1 parent 77ffc42 commit a6e9ad5

File tree

5 files changed

+205
-9
lines changed

5 files changed

+205
-9
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,5 @@ docs/_build/
5353
# PyBuilder
5454
target/
5555

56-
coverage/
56+
coverage/
57+
.idea/

aiohttp_jinja2/__init__.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections import Mapping
55
from aiohttp import web
66
from aiohttp.abc import AbstractView
7+
from .helpers import GLOBAL_HELPERS
78

89

910
__version__ = '0.13.0'
@@ -17,19 +18,17 @@
1718

1819

1920
def setup(app, *args, app_key=APP_KEY, context_processors=(),
20-
filters=None, **kwargs):
21+
filters=None, default_helpers=True, **kwargs):
2122
env = jinja2.Environment(*args, **kwargs)
23+
if default_helpers:
24+
env.globals.update(GLOBAL_HELPERS)
2225
if filters is not None:
2326
env.filters.update(filters)
2427
app[app_key] = env
2528
if context_processors:
2629
app[APP_CONTEXT_PROCESSORS_KEY] = context_processors
2730
app.middlewares.append(context_processors_middleware)
2831

29-
def url(__aiohttp_jinja2_route_name, **kwargs):
30-
return app.router[__aiohttp_jinja2_route_name].url_for(**kwargs)
31-
32-
env.globals['url'] = url
3332
env.globals['app'] = app
3433

3534
return env

aiohttp_jinja2/helpers.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
useful context functions, see
3+
http://jinja.pocoo.org/docs/dev/api/#jinja2.contextfunction
4+
"""
5+
import jinja2
6+
7+
8+
@jinja2.contextfunction
9+
def url_for(context, __route_name, **parts):
10+
"""
11+
filter for generating urls, see
12+
http://aiohttp.readthedocs.io/en/stable/web.html#reverse-url-constructing-using-named-resources
13+
14+
Usage: {{ url('the-view-name') }} might become "/path/to/view" or
15+
{{ url('item-details', id=123, query={'active': 'true'}) }} might become "/items/1?active=true".
16+
"""
17+
app = context['app']
18+
19+
query = None
20+
if 'query_' in parts:
21+
query = parts.pop('query_')
22+
23+
url = app.router[__route_name].url_for(**parts)
24+
if query:
25+
url = url.with_query(query)
26+
return url
27+
28+
29+
@jinja2.contextfunction
30+
def static_url(context, static_file_path):
31+
"""
32+
filter for generating urls for static files. NOTE: you'll need
33+
to set app['static_root_url'] to be used as the root for the urls returned.
34+
35+
Usage: {{ static('styles.css') }} might become "/static/styles.css" or "http://mycdn.example.com/styles.css"
36+
"""
37+
app = context['app']
38+
try:
39+
static_url = app['static_root_url']
40+
except KeyError:
41+
raise RuntimeError('app does not define a static root url '
42+
'"static_root_url", you need to set the url root '
43+
'with `app[\'static_root_url\'] = \'<static root>\'`.')
44+
return '{}/{}'.format(static_url.rstrip('/'), static_file_path.lstrip('/'))
45+
46+
47+
GLOBAL_HELPERS = dict(
48+
url=url_for,
49+
static=static_url,
50+
)

docs/index.rst

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,70 @@ As you can see, there is a built-in :func:`request_processor`, which
9797
adds current :class:`aiohttp.web.Request` into context of templates
9898
under ``'request'`` name.
9999

100-
By default there are two aiohttp specific :attr:`jinja2.Environment().globals` available in templates:
100+
Default Globals
101+
...............
101102

102-
- ``url``: Resolve a named route url
103-
- ``app``: Use any properties on the app instance
103+
.. highlight:: html+jinja
104+
105+
``app`` is always made in templates via :attr:`jinja2.Environment().globals`::
104106

105107
<body>
106108
<h1>Welcome to {{ app['name'] }}</h1>
109+
</body>
110+
111+
112+
Two more helpers are also enabled by default: ``url`` and ``static``.
113+
114+
``url`` can be used with just a view name::
115+
116+
<body>
107117
<a href="{{ url('index') }}">Index Page</a>
108118
</body>
109119

110120

121+
Or with arguments::
122+
123+
<body>
124+
<a href="{{ url('user', id=123) }}">User Page</a>
125+
</body>
126+
127+
A query can be added to the url with the special ``query_`` keyword argument::
128+
129+
<body>
130+
<a href="{{ url('user', id=123, query_={'foo': 'bar'}) }}">User Page</a>
131+
</body>
132+
133+
134+
For a view defined by ``app.router.add_get('/user-profile/{id}/', user, name='user')``, the above would give::
135+
136+
<body>
137+
<a href="/user-profile/123/?foo=bar">User Page</a>
138+
</body>
139+
140+
141+
This is useful as it would allow your static path to switch in deployment or testing with just one line.
142+
143+
The ``static`` function has similar usage, except it requires you to set ``static_root_url`` on the app
144+
145+
.. code-block:: ruby
146+
147+
app = web.Application()
148+
aiohttp_jinja2.setup(app,
149+
loader=jinja2.FileSystemLoader('/path/to/templates/folder'))
150+
app['static_root_url'] = '/static'
151+
152+
Then in the template::
153+
154+
<script src="{{ static('dist/main.js') }}"></script>
155+
156+
157+
Would result in::
158+
159+
<script src="/static/dist/main.js"></script>
160+
161+
162+
Both ``url`` and ``static`` can be disabled by passing ``default_helpers=False`` to ``aiohttp_jinja2.setup``.
163+
111164
Library Installation
112165
--------------------
113166

tests/test_jinja_globals.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,96 @@ def other(request):
4141
assert 200 == resp.status
4242
txt = yield from resp.text()
4343
assert '/user/John_Doe' == txt
44+
45+
46+
@asyncio.coroutine
47+
def test_url_with_query(test_client, loop):
48+
49+
@aiohttp_jinja2.template('tmpl.jinja2')
50+
@asyncio.coroutine
51+
def index(request):
52+
return {}
53+
54+
app = web.Application(loop=loop)
55+
aiohttp_jinja2.setup(app, loader=jinja2.DictLoader(
56+
{'tmpl.jinja2':
57+
"{{ url('index', query_={'foo': 'bar'})}}"}))
58+
59+
app.router.add_get('/', index, name='index')
60+
client = yield from test_client(app)
61+
62+
resp = yield from client.get('/')
63+
assert 200 == resp.status
64+
txt = yield from resp.text()
65+
assert '/?foo=bar' == txt
66+
67+
68+
@asyncio.coroutine
69+
def test_helpers_disabled(test_client, loop):
70+
71+
@aiohttp_jinja2.template('tmpl.jinja2')
72+
@asyncio.coroutine
73+
def index(request):
74+
return {}
75+
76+
@asyncio.coroutine
77+
def other(request):
78+
return
79+
80+
app = web.Application(loop=loop)
81+
aiohttp_jinja2.setup(
82+
app,
83+
default_helpers=False,
84+
loader=jinja2.DictLoader(
85+
{'tmpl.jinja2': "{{ url('index')}}"})
86+
)
87+
88+
app.router.add_route('GET', '/', index)
89+
app.router.add_route('GET', '/user/{name}', other, name='other')
90+
client = yield from test_client(app)
91+
92+
resp = yield from client.get('/')
93+
assert 500 == resp.status # url function is not defined
94+
95+
96+
@asyncio.coroutine
97+
def test_static(test_client, loop):
98+
99+
@aiohttp_jinja2.template('tmpl.jinja2')
100+
@asyncio.coroutine
101+
def index(request):
102+
return {}
103+
104+
app = web.Application(loop=loop)
105+
aiohttp_jinja2.setup(app, loader=jinja2.DictLoader(
106+
{'tmpl.jinja2':
107+
"{{ static('whatever.js') }}"}))
108+
109+
app['static_root_url'] = '/static'
110+
app.router.add_route('GET', '/', index)
111+
client = yield from test_client(app)
112+
113+
resp = yield from client.get('/')
114+
assert 200 == resp.status
115+
txt = yield from resp.text()
116+
assert '/static/whatever.js' == txt
117+
118+
119+
@asyncio.coroutine
120+
def test_static_var_missing(test_client, loop):
121+
122+
@aiohttp_jinja2.template('tmpl.jinja2')
123+
@asyncio.coroutine
124+
def index(request):
125+
return {}
126+
127+
app = web.Application(loop=loop)
128+
aiohttp_jinja2.setup(app, loader=jinja2.DictLoader(
129+
{'tmpl.jinja2':
130+
"{{ static('whatever.js') }}"}))
131+
132+
app.router.add_route('GET', '/', index)
133+
client = yield from test_client(app)
134+
135+
resp = yield from client.get('/')
136+
assert 500 == resp.status # static_root_url is not set

0 commit comments

Comments
 (0)