Fixed #24694 -- Added support for context_processors to Jinja2 backend.

This commit is contained in:
Berker Peksag 2016-06-27 16:56:21 +03:00 committed by Tim Graham
parent 222e1334bf
commit 52a991d976
4 changed files with 50 additions and 7 deletions

View File

@ -8,6 +8,7 @@ import jinja2
from django.conf import settings from django.conf import settings
from django.template import TemplateDoesNotExist, TemplateSyntaxError from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.utils import six from django.utils import six
from django.utils.functional import cached_property
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from .base import BaseEngine from .base import BaseEngine
@ -23,6 +24,8 @@ class Jinja2(BaseEngine):
options = params.pop('OPTIONS').copy() options = params.pop('OPTIONS').copy()
super(Jinja2, self).__init__(params) super(Jinja2, self).__init__(params)
self.context_processors = options.pop('context_processors', [])
environment = options.pop('environment', 'jinja2.Environment') environment = options.pop('environment', 'jinja2.Environment')
environment_cls = import_string(environment) environment_cls = import_string(environment)
@ -36,11 +39,11 @@ class Jinja2(BaseEngine):
self.env = environment_cls(**options) self.env = environment_cls(**options)
def from_string(self, template_code): def from_string(self, template_code):
return Template(self.env.from_string(template_code)) return Template(self.env.from_string(template_code), self)
def get_template(self, template_name): def get_template(self, template_name):
try: try:
return Template(self.env.get_template(template_name)) return Template(self.env.get_template(template_name), self)
except jinja2.TemplateNotFound as exc: except jinja2.TemplateNotFound as exc:
six.reraise( six.reraise(
TemplateDoesNotExist, TemplateDoesNotExist,
@ -52,11 +55,16 @@ class Jinja2(BaseEngine):
new.template_debug = get_exception_info(exc) new.template_debug = get_exception_info(exc)
six.reraise(TemplateSyntaxError, new, sys.exc_info()[2]) six.reraise(TemplateSyntaxError, new, sys.exc_info()[2])
@cached_property
def template_context_processors(self):
return [import_string(path) for path in self.context_processors]
class Template(object): class Template(object):
def __init__(self, template): def __init__(self, template, backend):
self.template = template self.template = template
self.backend = backend
self.origin = Origin( self.origin = Origin(
name=template.filename, template_name=template.name, name=template.filename, template_name=template.name,
) )
@ -68,6 +76,8 @@ class Template(object):
context['request'] = request context['request'] = request
context['csrf_input'] = csrf_input_lazy(request) context['csrf_input'] = csrf_input_lazy(request)
context['csrf_token'] = csrf_token_lazy(request) context['csrf_token'] = csrf_token_lazy(request)
for context_processor in self.backend.template_context_processors:
context.update(context_processor(request))
return self.template.render(context) return self.template.render(context)

View File

@ -220,6 +220,10 @@ Templates
* :meth:`~django.utils.safestring.mark_safe` can now be used as a decorator. * :meth:`~django.utils.safestring.mark_safe` can now be used as a decorator.
* The :class:`~django.template.backends.jinja2.Jinja2` template backend now
supports context processors by setting the ``'context_processors'`` option in
:setting:`OPTIONS <TEMPLATES-OPTIONS>`.
Tests Tests
~~~~~ ~~~~~

View File

@ -398,13 +398,27 @@ adds defaults that differ from Jinja2's for a few options:
* ``'auto_reload'``: ``settings.DEBUG`` * ``'auto_reload'``: ``settings.DEBUG``
* ``'undefined'``: ``DebugUndefined if settings.DEBUG else Undefined`` * ``'undefined'``: ``DebugUndefined if settings.DEBUG else Undefined``
``Jinja2`` engines also accept the following :setting:`OPTIONS
<TEMPLATES-OPTIONS>`:
* ``'context_processors'``: a list of dotted Python paths to callables that
are used to populate the context when a template is rendered with a request.
These callables take a request object as their argument and return a
:class:`dict` of items to be merged into the context.
It defaults to an empty list.
.. versionadded:: 1.11
The ``'context_processors'`` option was added.
The default configuration is purposefully kept to a minimum. If a template is The default configuration is purposefully kept to a minimum. If a template is
rendered with a request (e.g. when using :py:func:`~django.shortcuts.render`), rendered with a request (e.g. when using :py:func:`~django.shortcuts.render`),
the ``Jinja2`` backend adds the globals ``request``, ``csrf_input``, and the ``Jinja2`` backend adds the globals ``request``, ``csrf_input``, and
``csrf_token`` to the context. Apart from that, this backend doesn't create a ``csrf_token`` to the context. Apart from that, this backend doesn't create a
Django-flavored environment. It doesn't know about Django context processors, Django-flavored environment. It doesn't know about Django filters and tags.
filters, and tags. In order to use Django-specific APIs, you must configure In order to use Django-specific APIs, you must configure them into the
them into the environment. environment.
For example, you can create ``myproject/jinja2.py`` with this content:: For example, you can create ``myproject/jinja2.py`` with this content::

View File

@ -5,6 +5,7 @@ from __future__ import absolute_import
from unittest import skipIf from unittest import skipIf
from django.template import TemplateSyntaxError from django.template import TemplateSyntaxError
from django.test import RequestFactory
from .test_dummy import TemplateStringsTests from .test_dummy import TemplateStringsTests
@ -22,7 +23,12 @@ class Jinja2Tests(TemplateStringsTests):
engine_class = Jinja2 engine_class = Jinja2
backend_name = 'jinja2' backend_name = 'jinja2'
options = {'keep_trailing_newline': True} options = {
'keep_trailing_newline': True,
'context_processors': [
'django.template.context_processors.static',
],
}
def test_origin(self): def test_origin(self):
template = self.engine.get_template('template_backends/hello.html') template = self.engine.get_template('template_backends/hello.html')
@ -74,3 +80,12 @@ class Jinja2Tests(TemplateStringsTests):
self.assertEqual(len(debug['source_lines']), 21) self.assertEqual(len(debug['source_lines']), 21)
self.assertTrue(debug['name'].endswith('syntax_error2.html')) self.assertTrue(debug['name'].endswith('syntax_error2.html'))
self.assertIn('message', debug) self.assertIn('message', debug)
def test_context_processors(self):
request = RequestFactory().get('/')
template = self.engine.from_string('Static URL: {{ STATIC_URL }}')
content = template.render(request=request)
self.assertEqual(content, 'Static URL: /static/')
with self.settings(STATIC_URL='/s/'):
content = template.render(request=request)
self.assertEqual(content, 'Static URL: /s/')