This commit is contained in:
Jeff Balogh 2010-02-09 16:36:24 -08:00
Родитель fa330b13e4
Коммит 6e048e3801
8 изменённых файлов: 1 добавлений и 287 удалений

Просмотреть файл

@ -1,59 +0,0 @@
.. _jingo:
.. module:: jingo
.. highlight:: jinja
A page about Jingo.
===================
Jingo is an adapter for using
`Jinja2 <http://jinja.pocoo.org/2/documentation/>`_ templates within Django.
Why are we already replacing the templates? AMO's current PHP templates let you
go hog-wild with logic in the templates, while Django is extremely restrictive.
Jinja loosens those restrictions somewhat, providing a more powerful engine with
the beauty of Django's templates. The tipping point for me was the verbosity of
doing L10n in Django templates.
Localization
------------
Since we all love L10n, let's see what it looks like in Jinja templates::
<h2>{{ _('Reviews for {0}')|f(addon.name) }}</h2>
The simple way is to use the familiar underscore and string within a ``{{ }}``
moustache block. ``f`` is an interpolation filter documented below. Sphinx
could create a link if I knew how to do that.
The other method uses Jinja's ``trans`` tag::
{% trans user=review.user|user_link, date=review.created|datetime %}
by {{ user }} on {{ date }}
{% endtrans %}
``trans`` is nice when you have a lot of text or want to inject some variables
directly. Both methods are useful, pick the one that makes you happy.
Rendering
---------
At this point, Jingo only provides two shortcuts for rendering templates.
.. autofunction:: jingo.render
The basic usage is to pass an ``HttpRequest`` and a template name. All the
processors in ``settings.CONTEXT_PROCESSORS`` will be applied to the
context, just like when you use a ``RequestContext`` in Django. Any extra
keyword arguments are passed directly to ``http.HttpResponse``.
Template filters provided by jingo
----------------------------------
These filters are injected into all templates automatically. Template filters
are what Jinja uses instead of "helpers" in other systems. They're just
functions that get called from templates using the ``|`` (pipe) syntax.
.. automodule:: jingo.helpers
:members:

Просмотреть файл

@ -1,91 +0,0 @@
"""Adapter for using Jinja2 with Django."""
from django import http
from django.conf import settings
from django.template.context import get_standard_processors
import jinja2
def get_env():
"""Configure and return a jinja2 Environment."""
# Mimic Django's setup by loading templates from directories in
# TEMPLATE_DIRS and packages in INSTALLED_APPS.
x = ((jinja2.FileSystemLoader, settings.TEMPLATE_DIRS),
(jinja2.PackageLoader, settings.INSTALLED_APPS))
loaders = [loader(p) for loader, places in x for p in places]
opts = {'trim_blocks': True,
'extensions': ['jinja2.ext.i18n'],
'autoescape': True,
'auto_reload': settings.DEBUG,
'loader': jinja2.ChoiceLoader(loaders),
}
if hasattr(settings, 'JINJA_CONFIG'):
if hasattr(settings.JINJA_CONFIG, '__call__'):
config = settings.JINJA_CONFIG()
else:
config = settings.JINJA_CONFIG
opts.update(config)
e = jinja2.Environment(**opts)
# TODO: use real translations
e.install_null_translations()
return e
def render(request, template, context=None, **kwargs):
"""
Shortcut like Django's render_to_response, but better.
Minimal usage, with only a request object and a template name::
return jingo.render(request, 'template.html')
With template context and keywords passed to
:class:`django.http.HttpResponse`::
return jingo.render(request, 'template.html',
{'some_var': 42}, status=209)
"""
if context is None:
context = {}
for processor in get_standard_processors():
context.update(processor(request))
rendered = env.get_template(template).render(**context)
return http.HttpResponse(rendered, **kwargs)
def load_helpers():
"""Try to import ``helpers.py`` from each app in INSTALLED_APPS."""
for app in settings.INSTALLED_APPS:
try:
__import__('%s.helpers' % app)
except ImportError:
pass
class Register(object):
"""Decorators to add filters and functions to the template Environment."""
def __init__(self, env):
self.env = env
def filter(self, f):
"""Adds the decorated function to Jinja's filter library."""
self.env.filters[f.__name__] = f
return f
def function(self, f):
"""Adds the decorated function to Jinja's global namespace."""
self.env.globals[f.__name__] = f
return f
env = get_env()
register = Register(env)
# Import down here after the env is initialized.
from . import helpers
load_helpers()

Просмотреть файл

@ -1,46 +0,0 @@
from django.utils.translation import ugettext as _
from django.template.defaulttags import CsrfTokenNode
import jinja2
from jingo import register
@register.function
@jinja2.contextfunction
def csrf(context):
return jinja2.Markup(CsrfTokenNode().render(context))
@register.filter
def f(string, *args, **kwargs):
"""
Uses ``str.format`` for string interpolation.
>>> {{ "{0} arguments and {x} arguments"|f('positional', x='keyword') }}
"positional arguments and keyword arguments"
"""
string = unicode(string)
return string.format(*args, **kwargs)
@register.filter
def nl2br(string):
return jinja2.Markup('<br>'.join(jinja2.escape(string).splitlines()))
@register.filter
def datetime(t, format=_('%B %d, %Y')):
return t.strftime(format)
@register.filter
def ifeq(a, b, text):
"""Return ``text`` if ``a == b``."""
return jinja2.Markup(text if a == b else '')
@register.filter
def class_selected(a, b):
"""Return ``'class="selected"'`` if ``a == b``."""
return ifeq(a, b, 'class="selected"')

Просмотреть файл

@ -1,16 +0,0 @@
from nose.tools import eq_
from mock import Mock, patch, sentinel
import jingo
@patch('jingo.env')
def test_render(mock_env):
mock_template = Mock()
mock_env.get_template.return_value = mock_template
response = jingo.render(Mock(), sentinel.template, status=32)
mock_env.get_template.assert_called_with(sentinel.template)
assert mock_template.render.called
eq_(response.status_code, 32)

Просмотреть файл

@ -1,60 +0,0 @@
"""Tests for the jingo's builtin helpers."""
from datetime import datetime
from nose.tools import eq_
import jingo
def render(s, context={}):
t = jingo.env.from_string(s)
return t.render(**context)
def test_f():
s = render('{{ "{0} : {z}"|f("a", z="b") }}')
eq_(s, 'a : b')
def test_nl2br():
text = "some\ntext\n\nwith\nnewlines"
s = render('{{ x|nl2br }}', {'x': text})
eq_(s, "some<br>text<br><br>with<br>newlines")
def test_datetime():
time = datetime(2009, 12, 25, 10, 11, 12)
s = render('{{ d|datetime }}', {'d': time})
eq_(s, 'December 25, 2009')
s = render('{{ d|datetime("%Y-%m-%d %H:%M:%S") }}', {'d': time})
eq_(s, '2009-12-25 10:11:12')
def test_ifeq():
eq_context = {'a': 1, 'b': 1}
neq_context = {'a': 1, 'b': 2}
s = render('{{ a|ifeq(b, "<b>something</b>") }}', eq_context)
eq_(s, '<b>something</b>')
s = render('{{ a|ifeq(b, "<b>something</b>") }}', neq_context)
eq_(s, '')
def test_class_selected():
eq_context = {'a': 1, 'b': 1}
neq_context = {'a': 1, 'b': 2}
s = render('{{ a|class_selected(b) }}', eq_context)
eq_(s, 'class="selected"')
s = render('{{ a|class_selected(b) }}', neq_context)
eq_(s, '')
def test_csrf():
s = render('{{ csrf() }}', {'csrf_token': 'fffuuu'})
eq_(s, "<div style='display:none'>"
"<input type='hidden' name='csrfmiddlewaretoken' value='fffuuu' />"
"</div>")

Просмотреть файл

@ -1,10 +0,0 @@
from mock import patch, sentinel
import jingo.views
@patch('jingo.render')
def test_direct_to_template(mock_render):
request = sentinel.request
jingo.views.direct_to_template(request, 'base.html', x=1)
mock_render.assert_called_with(request, 'base.html', {'x': 1})

Просмотреть файл

@ -1,5 +0,0 @@
import jingo
def direct_to_template(request, template, **kwargs):
return jingo.render(request, template, kwargs)

Просмотреть файл

@ -8,3 +8,4 @@ south
-e git://github.com/jbalogh/django-multidb-router.git#egg=django-multidb-router
-e git://github.com/jbalogh/django-cache-machine.git#egg=django-cache-machine
-e git://github.com/jbalogh/jingo.git#egg=jingo