summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2015-02-17 22:49:59 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2015-02-19 22:10:56 +0100
commitcc4effba0bf07fbcb4dcc0074af969e698b6cc75 (patch)
treeefa5c227513168711732b0d14db6b09f62a21e4e
parent84e7fec88ddfc4178500a80d640728226d77317a (diff)
[1.8.x] Set context.template instead of context.engine while rendering.
This opens more possibilities, like accessing context.template.origin. It also follows the chain of objects instead of following a shortcut. Backport of 1bfcc95 from master
-rw-r--r--django/template/base.py24
-rw-r--r--django/template/context.py30
-rw-r--r--django/template/defaulttags.py6
-rw-r--r--django/template/loader_tags.py6
-rw-r--r--django/templatetags/i18n.py2
-rw-r--r--docs/howto/custom-template-tags.txt22
-rw-r--r--docs/ref/templates/upgrading.txt4
-rw-r--r--tests/template_tests/tests.py7
8 files changed, 53 insertions, 48 deletions
diff --git a/django/template/base.py b/django/template/base.py
index b9fd47625b..76f77fc793 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -202,19 +202,19 @@ class Template(object):
def render(self, context):
"Display stage -- can be called many times"
- # Set engine attribute here to avoid changing the signature of either
- # Context.__init__ or Node.render. The engine is set only on the first
- # call to render. Further calls e.g. for includes don't override it.
- toplevel_render = context.engine is None
+ # Set context.template to the original template -- as opposed to
+ # extended or included templates -- during rendering. This may be
+ # used for accessing context.template.engine.
+ toplevel_render = context.template is None
if toplevel_render:
- context.engine = self.engine
+ context.template = self
context.render_context.push()
try:
return self._render(context)
finally:
context.render_context.pop()
if toplevel_render:
- context.engine = None
+ context.template = None
class Token(object):
@@ -653,7 +653,7 @@ class FilterExpression(object):
if ignore_failures:
obj = None
else:
- string_if_invalid = context.engine.string_if_invalid
+ string_if_invalid = context.template.engine.string_if_invalid
if string_if_invalid:
if '%s' in string_if_invalid:
return string_if_invalid % self.var
@@ -845,7 +845,7 @@ class Variable(object):
if getattr(current, 'do_not_call_in_templates', False):
pass
elif getattr(current, 'alters_data', False):
- current = context.engine.string_if_invalid
+ current = context.template.engine.string_if_invalid
else:
try: # method call (assuming no args required)
current = current()
@@ -853,12 +853,12 @@ class Variable(object):
try:
getcallargs(current)
except TypeError: # arguments *were* required
- current = context.engine.string_if_invalid # invalid method call
+ current = context.template.engine.string_if_invalid # invalid method call
else:
raise
except Exception as e:
if getattr(e, 'silent_variable_failure', False):
- current = context.engine.string_if_invalid
+ current = context.template.engine.string_if_invalid
else:
raise
@@ -1275,9 +1275,9 @@ class Library(object):
elif isinstance(getattr(file_name, 'template', None), Template):
t = file_name.template
elif not isinstance(file_name, six.string_types) and is_iterable(file_name):
- t = context.engine.select_template(file_name)
+ t = context.template.engine.select_template(file_name)
else:
- t = context.engine.get_template(file_name)
+ t = context.template.engine.get_template(file_name)
self.nodelist = t.nodelist
new_context = context.new(_dict)
# Copy across the CSRF token, if present, because
diff --git a/django/template/context.py b/django/template/context.py
index 01d21a159a..b4983d1909 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -123,7 +123,7 @@ class Context(BaseContext):
"A stack container for variable context"
def __init__(self, dict_=None, autoescape=True,
current_app=_current_app_undefined,
- use_l10n=None, use_tz=None, engine=None):
+ use_l10n=None, use_tz=None):
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of Context is deprecated. Use "
@@ -133,8 +133,10 @@ class Context(BaseContext):
self._current_app = current_app
self.use_l10n = use_l10n
self.use_tz = use_tz
- self.engine = engine
self.render_context = RenderContext()
+ # Set to the original template during rendering -- as opposed to
+ # extended or included templates
+ self.template = None
super(Context, self).__init__(dict_)
@property
@@ -192,11 +194,11 @@ class RequestContext(Context):
"""
def __init__(self, request, dict_=None, processors=None,
current_app=_current_app_undefined,
- use_l10n=None, use_tz=None, engine=None):
+ use_l10n=None, use_tz=None):
# current_app isn't passed here to avoid triggering the deprecation
# warning in Context.__init__.
super(RequestContext, self).__init__(
- dict_, use_l10n=use_l10n, use_tz=use_tz, engine=engine)
+ dict_, use_l10n=use_l10n, use_tz=use_tz)
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of RequestContext is deprecated. "
@@ -207,23 +209,27 @@ class RequestContext(Context):
self._processors = () if processors is None else tuple(processors)
self._processors_index = len(self.dicts)
self.update({}) # placeholder for context processors output
- self.engine = engine # re-run the setter in case engine is not None
@property
- def engine(self):
- return self._engine
+ def template(self):
+ return self._template
- @engine.setter
- def engine(self, engine):
- self._engine = engine
+ @template.setter
+ def template(self, template):
+ # Execute context processors when Template.render(self, context) sets
+ # context.template = self. Until then, since the context isn't tied to
+ # an engine, it has no way to know which context processors to apply.
+ self._template = template
if hasattr(self, '_processors_index'):
- if engine is None:
+ if template is None:
# Unset context processors.
self.dicts[self._processors_index] = {}
else:
# Set context processors for this engine.
+ processors = (template.engine.template_context_processors +
+ self._processors)
updates = {}
- for processor in engine.template_context_processors + self._processors:
+ for processor in processors:
updates.update(processor(self.request))
self.dicts[self._processors_index] = updates
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 0dc6735ad6..fc8baf9611 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -211,7 +211,7 @@ class ForNode(Node):
context[self.loopvars[0]] = item
# In debug mode provide the source of the node which raised
# the exception
- if context.engine.debug:
+ if context.template.engine.debug:
for node in self.nodelist_loop:
try:
nodelist.append(node.render(context))
@@ -392,7 +392,7 @@ class SsiNode(Node):
def render(self, context):
filepath = self.filepath.resolve(context)
- if not include_is_allowed(filepath, context.engine.allowed_include_roots):
+ if not include_is_allowed(filepath, context.template.engine.allowed_include_roots):
if settings.DEBUG:
return "[Didn't have permission to include file]"
else:
@@ -404,7 +404,7 @@ class SsiNode(Node):
output = ''
if self.parsed:
try:
- t = Template(output, name=filepath, engine=context.engine)
+ t = Template(output, name=filepath, engine=context.template.engine)
return t.render(context)
except TemplateSyntaxError as e:
if settings.DEBUG:
diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index e6635258a7..398914b6b7 100644
--- a/django/template/loader_tags.py
+++ b/django/template/loader_tags.py
@@ -107,7 +107,7 @@ class ExtendsNode(Node):
if isinstance(getattr(parent, 'template', None), Template):
# parent is a django.template.backends.django.Template
return parent.template
- return context.engine.get_template(parent)
+ return context.template.engine.get_template(parent)
def render(self, context):
compiled_parent = self.get_parent(context)
@@ -148,7 +148,7 @@ class IncludeNode(Node):
# Does this quack like a Template?
if not callable(getattr(template, 'render', None)):
# If not, we'll try get_template
- template = context.engine.get_template(template)
+ template = context.template.engine.get_template(template)
values = {
name: var.resolve(context)
for name, var in six.iteritems(self.extra_context)
@@ -158,7 +158,7 @@ class IncludeNode(Node):
with context.push(**values):
return template.render(context)
except Exception:
- if context.engine.debug:
+ if context.template.engine.debug:
raise
return ''
diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py
index b30d6afa95..8c6746c680 100644
--- a/django/templatetags/i18n.py
+++ b/django/templatetags/i18n.py
@@ -149,7 +149,7 @@ class BlockTranslateNode(Node):
result = translation.pgettext(message_context, singular)
else:
result = translation.ugettext(singular)
- default_value = context.engine.string_if_invalid
+ default_value = context.template.engine.string_if_invalid
def render_value(key):
if key in context:
diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt
index d8fde58ffd..ca21e16549 100644
--- a/docs/howto/custom-template-tags.txt
+++ b/docs/howto/custom-template-tags.txt
@@ -772,10 +772,11 @@ Notes:
* The ``render()`` method is where the work actually happens.
* ``render()`` should generally fail silently, particularly in a production
- environment. In some cases however, particularly if ``context.engine.debug``
- is ``True``, this method may raise an exception to make debugging easier.
- For example, several core tags raise ``django.template.TemplateSyntaxError``
- if they receive the wrong number or type of arguments.
+ environment. In some cases however, particularly if
+ ``context.template.engine.debug`` is ``True``, this method may raise an
+ exception to make debugging easier. For example, several core tags raise
+ ``django.template.TemplateSyntaxError`` if they receive the wrong number or
+ type of arguments.
Ultimately, this decoupling of compilation and rendering results in an
efficient template system, because a template can render multiple contexts
@@ -811,16 +812,17 @@ This is not a very common situation, but it's useful if you're rendering a
template yourself. For example::
def render(self, context):
- t = context.engine.get_template('small_fragment.html')
+ t = context.template.engine.get_template('small_fragment.html')
return t.render(Context({'var': obj}, autoescape=context.autoescape))
.. versionchanged:: 1.8
- The ``engine`` attribute of ``Context`` objects was added in Django 1.8.
- :meth:`context.engine.get_template <django.template.Engine.get_template>`
- must be used instead of :func:`django.template.loader.get_template`
- because the latter now returns a wrapper whose ``render`` method doesn't
- accept a :class:`~django.template.Context`.
+ The ``template`` attribute of ``Context`` objects was added in Django 1.8.
+ :meth:`context.template.engine.get_template
+ <django.template.Engine.get_template>` must be used instead of
+ :func:`django.template.loader.get_template` because the latter now returns
+ a wrapper whose ``render`` method doesn't accept a
+ :class:`~django.template.Context`.
If we had neglected to pass in the current ``context.autoescape`` value to our
new ``Context`` in this example, the results would have *always* been
diff --git a/docs/ref/templates/upgrading.txt b/docs/ref/templates/upgrading.txt
index 98778c36ba..33057d36bd 100644
--- a/docs/ref/templates/upgrading.txt
+++ b/docs/ref/templates/upgrading.txt
@@ -162,7 +162,7 @@ instance in the ``render()`` method of a template tag, you can use the current
You can write::
- template = context.engine.get_template('included.html')
+ template = context.template.engine.get_template('included.html')
This will load the template with the current engine without triggering the
multiple template engines machinery, which is usually the desired behavior.
@@ -201,7 +201,7 @@ APIs. The multiple template engines machinery isn't involved here.
Finally, if you have access to the current context, you can use the same trick
as above::
- template = context.engine.from_string(template_code)
+ template = context.template.engine.from_string(template_code)
``Template()``
==============
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index cf37e1868b..77115d061a 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -516,9 +516,6 @@ class RequestContextTests(unittest.TestCase):
self.assertEqual(len(ctx.dicts), 3)
def test_context_comparable(self):
- # Create an engine without any context processors.
- engine = Engine()
-
test_data = {'x': 'y', 'v': 'z', 'd': {'o': object, 'a': 'b'}}
# test comparing RequestContext to prevent problems if somebody
@@ -526,8 +523,8 @@ class RequestContextTests(unittest.TestCase):
request = RequestFactory().get('/')
self.assertEqual(
- RequestContext(request, dict_=test_data, engine=engine),
- RequestContext(request, dict_=test_data, engine=engine))
+ RequestContext(request, dict_=test_data),
+ RequestContext(request, dict_=test_data))
@ignore_warnings(category=RemovedInDjango20Warning)