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.
This commit is contained in:
parent
efb1f99f94
commit
1bfcc950ab
|
@ -204,19 +204,19 @@ class Template(object):
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
"Display stage -- can be called many times"
|
"Display stage -- can be called many times"
|
||||||
# Set engine attribute here to avoid changing the signature of either
|
# Set context.template to the original template -- as opposed to
|
||||||
# Context.__init__ or Node.render. The engine is set only on the first
|
# extended or included templates -- during rendering. This may be
|
||||||
# call to render. Further calls e.g. for includes don't override it.
|
# used for accessing context.template.engine.
|
||||||
toplevel_render = context.engine is None
|
toplevel_render = context.template is None
|
||||||
if toplevel_render:
|
if toplevel_render:
|
||||||
context.engine = self.engine
|
context.template = self
|
||||||
context.render_context.push()
|
context.render_context.push()
|
||||||
try:
|
try:
|
||||||
return self._render(context)
|
return self._render(context)
|
||||||
finally:
|
finally:
|
||||||
context.render_context.pop()
|
context.render_context.pop()
|
||||||
if toplevel_render:
|
if toplevel_render:
|
||||||
context.engine = None
|
context.template = None
|
||||||
|
|
||||||
|
|
||||||
class Token(object):
|
class Token(object):
|
||||||
|
@ -655,7 +655,7 @@ class FilterExpression(object):
|
||||||
if ignore_failures:
|
if ignore_failures:
|
||||||
obj = None
|
obj = None
|
||||||
else:
|
else:
|
||||||
string_if_invalid = context.engine.string_if_invalid
|
string_if_invalid = context.template.engine.string_if_invalid
|
||||||
if string_if_invalid:
|
if string_if_invalid:
|
||||||
if '%s' in string_if_invalid:
|
if '%s' in string_if_invalid:
|
||||||
return string_if_invalid % self.var
|
return string_if_invalid % self.var
|
||||||
|
@ -847,7 +847,7 @@ class Variable(object):
|
||||||
if getattr(current, 'do_not_call_in_templates', False):
|
if getattr(current, 'do_not_call_in_templates', False):
|
||||||
pass
|
pass
|
||||||
elif getattr(current, 'alters_data', False):
|
elif getattr(current, 'alters_data', False):
|
||||||
current = context.engine.string_if_invalid
|
current = context.template.engine.string_if_invalid
|
||||||
else:
|
else:
|
||||||
try: # method call (assuming no args required)
|
try: # method call (assuming no args required)
|
||||||
current = current()
|
current = current()
|
||||||
|
@ -855,12 +855,12 @@ class Variable(object):
|
||||||
try:
|
try:
|
||||||
getcallargs(current)
|
getcallargs(current)
|
||||||
except TypeError: # arguments *were* required
|
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:
|
else:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if getattr(e, 'silent_variable_failure', False):
|
if getattr(e, 'silent_variable_failure', False):
|
||||||
current = context.engine.string_if_invalid
|
current = context.template.engine.string_if_invalid
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -1257,9 +1257,9 @@ class Library(object):
|
||||||
elif isinstance(getattr(file_name, 'template', None), Template):
|
elif isinstance(getattr(file_name, 'template', None), Template):
|
||||||
t = file_name.template
|
t = file_name.template
|
||||||
elif not isinstance(file_name, six.string_types) and is_iterable(file_name):
|
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:
|
else:
|
||||||
t = context.engine.get_template(file_name)
|
t = context.template.engine.get_template(file_name)
|
||||||
self.nodelist = t.nodelist
|
self.nodelist = t.nodelist
|
||||||
new_context = context.new(_dict)
|
new_context = context.new(_dict)
|
||||||
# Copy across the CSRF token, if present, because
|
# Copy across the CSRF token, if present, because
|
||||||
|
|
|
@ -123,7 +123,7 @@ class Context(BaseContext):
|
||||||
"A stack container for variable context"
|
"A stack container for variable context"
|
||||||
def __init__(self, dict_=None, autoescape=True,
|
def __init__(self, dict_=None, autoescape=True,
|
||||||
current_app=_current_app_undefined,
|
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:
|
if current_app is not _current_app_undefined:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"The current_app argument of Context is deprecated. Use "
|
"The current_app argument of Context is deprecated. Use "
|
||||||
|
@ -133,8 +133,10 @@ class Context(BaseContext):
|
||||||
self._current_app = current_app
|
self._current_app = current_app
|
||||||
self.use_l10n = use_l10n
|
self.use_l10n = use_l10n
|
||||||
self.use_tz = use_tz
|
self.use_tz = use_tz
|
||||||
self.engine = engine
|
|
||||||
self.render_context = RenderContext()
|
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_)
|
super(Context, self).__init__(dict_)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -192,11 +194,11 @@ class RequestContext(Context):
|
||||||
"""
|
"""
|
||||||
def __init__(self, request, dict_=None, processors=None,
|
def __init__(self, request, dict_=None, processors=None,
|
||||||
current_app=_current_app_undefined,
|
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
|
# current_app isn't passed here to avoid triggering the deprecation
|
||||||
# warning in Context.__init__.
|
# warning in Context.__init__.
|
||||||
super(RequestContext, self).__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:
|
if current_app is not _current_app_undefined:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"The current_app argument of RequestContext is deprecated. "
|
"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 = () if processors is None else tuple(processors)
|
||||||
self._processors_index = len(self.dicts)
|
self._processors_index = len(self.dicts)
|
||||||
self.update({}) # placeholder for context processors output
|
self.update({}) # placeholder for context processors output
|
||||||
self.engine = engine # re-run the setter in case engine is not None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def engine(self):
|
def template(self):
|
||||||
return self._engine
|
return self._template
|
||||||
|
|
||||||
@engine.setter
|
@template.setter
|
||||||
def engine(self, engine):
|
def template(self, template):
|
||||||
self._engine = engine
|
# 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 hasattr(self, '_processors_index'):
|
||||||
if engine is None:
|
if template is None:
|
||||||
# Unset context processors.
|
# Unset context processors.
|
||||||
self.dicts[self._processors_index] = {}
|
self.dicts[self._processors_index] = {}
|
||||||
else:
|
else:
|
||||||
# Set context processors for this engine.
|
# Set context processors for this engine.
|
||||||
|
processors = (template.engine.template_context_processors +
|
||||||
|
self._processors)
|
||||||
updates = {}
|
updates = {}
|
||||||
for processor in engine.template_context_processors + self._processors:
|
for processor in processors:
|
||||||
updates.update(processor(self.request))
|
updates.update(processor(self.request))
|
||||||
self.dicts[self._processors_index] = updates
|
self.dicts[self._processors_index] = updates
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ class ForNode(Node):
|
||||||
context[self.loopvars[0]] = item
|
context[self.loopvars[0]] = item
|
||||||
# In debug mode provide the source of the node which raised
|
# In debug mode provide the source of the node which raised
|
||||||
# the exception
|
# the exception
|
||||||
if context.engine.debug:
|
if context.template.engine.debug:
|
||||||
for node in self.nodelist_loop:
|
for node in self.nodelist_loop:
|
||||||
try:
|
try:
|
||||||
nodelist.append(node.render(context))
|
nodelist.append(node.render(context))
|
||||||
|
@ -392,7 +392,7 @@ class SsiNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
filepath = self.filepath.resolve(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:
|
if settings.DEBUG:
|
||||||
return "[Didn't have permission to include file]"
|
return "[Didn't have permission to include file]"
|
||||||
else:
|
else:
|
||||||
|
@ -404,7 +404,7 @@ class SsiNode(Node):
|
||||||
output = ''
|
output = ''
|
||||||
if self.parsed:
|
if self.parsed:
|
||||||
try:
|
try:
|
||||||
t = Template(output, name=filepath, engine=context.engine)
|
t = Template(output, name=filepath, engine=context.template.engine)
|
||||||
return t.render(context)
|
return t.render(context)
|
||||||
except TemplateSyntaxError as e:
|
except TemplateSyntaxError as e:
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
@ -107,7 +107,7 @@ class ExtendsNode(Node):
|
||||||
if isinstance(getattr(parent, 'template', None), Template):
|
if isinstance(getattr(parent, 'template', None), Template):
|
||||||
# parent is a django.template.backends.django.Template
|
# parent is a django.template.backends.django.Template
|
||||||
return parent.template
|
return parent.template
|
||||||
return context.engine.get_template(parent)
|
return context.template.engine.get_template(parent)
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
compiled_parent = self.get_parent(context)
|
compiled_parent = self.get_parent(context)
|
||||||
|
@ -148,7 +148,7 @@ class IncludeNode(Node):
|
||||||
# Does this quack like a Template?
|
# Does this quack like a Template?
|
||||||
if not callable(getattr(template, 'render', None)):
|
if not callable(getattr(template, 'render', None)):
|
||||||
# If not, we'll try get_template
|
# If not, we'll try get_template
|
||||||
template = context.engine.get_template(template)
|
template = context.template.engine.get_template(template)
|
||||||
values = {
|
values = {
|
||||||
name: var.resolve(context)
|
name: var.resolve(context)
|
||||||
for name, var in six.iteritems(self.extra_context)
|
for name, var in six.iteritems(self.extra_context)
|
||||||
|
@ -158,7 +158,7 @@ class IncludeNode(Node):
|
||||||
with context.push(**values):
|
with context.push(**values):
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
except Exception:
|
except Exception:
|
||||||
if context.engine.debug:
|
if context.template.engine.debug:
|
||||||
raise
|
raise
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ class BlockTranslateNode(Node):
|
||||||
result = translation.pgettext(message_context, singular)
|
result = translation.pgettext(message_context, singular)
|
||||||
else:
|
else:
|
||||||
result = translation.ugettext(singular)
|
result = translation.ugettext(singular)
|
||||||
default_value = context.engine.string_if_invalid
|
default_value = context.template.engine.string_if_invalid
|
||||||
|
|
||||||
def render_value(key):
|
def render_value(key):
|
||||||
if key in context:
|
if key in context:
|
||||||
|
|
|
@ -756,10 +756,11 @@ Notes:
|
||||||
* The ``render()`` method is where the work actually happens.
|
* The ``render()`` method is where the work actually happens.
|
||||||
|
|
||||||
* ``render()`` should generally fail silently, particularly in a production
|
* ``render()`` should generally fail silently, particularly in a production
|
||||||
environment. In some cases however, particularly if ``context.engine.debug``
|
environment. In some cases however, particularly if
|
||||||
is ``True``, this method may raise an exception to make debugging easier.
|
``context.template.engine.debug`` is ``True``, this method may raise an
|
||||||
For example, several core tags raise ``django.template.TemplateSyntaxError``
|
exception to make debugging easier. For example, several core tags raise
|
||||||
if they receive the wrong number or type of arguments.
|
``django.template.TemplateSyntaxError`` if they receive the wrong number or
|
||||||
|
type of arguments.
|
||||||
|
|
||||||
Ultimately, this decoupling of compilation and rendering results in an
|
Ultimately, this decoupling of compilation and rendering results in an
|
||||||
efficient template system, because a template can render multiple contexts
|
efficient template system, because a template can render multiple contexts
|
||||||
|
@ -795,16 +796,17 @@ This is not a very common situation, but it's useful if you're rendering a
|
||||||
template yourself. For example::
|
template yourself. For example::
|
||||||
|
|
||||||
def render(self, context):
|
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))
|
return t.render(Context({'var': obj}, autoescape=context.autoescape))
|
||||||
|
|
||||||
.. versionchanged:: 1.8
|
.. versionchanged:: 1.8
|
||||||
|
|
||||||
The ``engine`` attribute of ``Context`` objects was added in Django 1.8.
|
The ``template`` attribute of ``Context`` objects was added in Django 1.8.
|
||||||
:meth:`context.engine.get_template <django.template.Engine.get_template>`
|
:meth:`context.template.engine.get_template
|
||||||
must be used instead of :func:`django.template.loader.get_template`
|
<django.template.Engine.get_template>` must be used instead of
|
||||||
because the latter now returns a wrapper whose ``render`` method doesn't
|
:func:`django.template.loader.get_template` because the latter now returns
|
||||||
accept a :class:`~django.template.Context`.
|
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
|
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
|
new ``Context`` in this example, the results would have *always* been
|
||||||
|
|
|
@ -162,7 +162,7 @@ instance in the ``render()`` method of a template tag, you can use the current
|
||||||
|
|
||||||
You can write::
|
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
|
This will load the template with the current engine without triggering the
|
||||||
multiple template engines machinery, which is usually the desired behavior.
|
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
|
Finally, if you have access to the current context, you can use the same trick
|
||||||
as above::
|
as above::
|
||||||
|
|
||||||
template = context.engine.from_string(template_code)
|
template = context.template.engine.from_string(template_code)
|
||||||
|
|
||||||
``Template()``
|
``Template()``
|
||||||
==============
|
==============
|
||||||
|
|
|
@ -516,9 +516,6 @@ class RequestContextTests(unittest.TestCase):
|
||||||
self.assertEqual(len(ctx.dicts), 3)
|
self.assertEqual(len(ctx.dicts), 3)
|
||||||
|
|
||||||
def test_context_comparable(self):
|
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_data = {'x': 'y', 'v': 'z', 'd': {'o': object, 'a': 'b'}}
|
||||||
|
|
||||||
# test comparing RequestContext to prevent problems if somebody
|
# test comparing RequestContext to prevent problems if somebody
|
||||||
|
@ -526,8 +523,8 @@ class RequestContextTests(unittest.TestCase):
|
||||||
request = RequestFactory().get('/')
|
request = RequestFactory().get('/')
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
RequestContext(request, dict_=test_data, engine=engine),
|
RequestContext(request, dict_=test_data),
|
||||||
RequestContext(request, dict_=test_data, engine=engine))
|
RequestContext(request, dict_=test_data))
|
||||||
|
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango20Warning)
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
|
|
Loading…
Reference in New Issue