diff --git a/django/template/base.py b/django/template/base.py index 325cd7e560eea..d89021d7c0bca 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -204,19 +204,15 @@ def _render(self, context): def render(self, context): "Display stage -- can be called many times" - # 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.template = self context.render_context.push() try: - return self._render(context) + if context.template is None: + with context.bind_template(self): + return self._render(context) + else: + return self._render(context) finally: context.render_context.pop() - if toplevel_render: - context.template = None class Token(object): diff --git a/django/template/context.py b/django/template/context.py index b4983d1909810..af4fb8853b845 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -1,4 +1,5 @@ import warnings +from contextlib import contextmanager from copy import copy from django.utils.deprecation import RemovedInDjango20Warning @@ -134,8 +135,8 @@ def __init__(self, dict_=None, autoescape=True, self.use_l10n = use_l10n self.use_tz = use_tz self.render_context = RenderContext() - # Set to the original template during rendering -- as opposed to - # extended or included templates + # Set to the original template -- as opposed to extended or included + # templates -- during rendering, see bind_template. self.template = None super(Context, self).__init__(dict_) @@ -143,6 +144,16 @@ def __init__(self, dict_=None, autoescape=True, def current_app(self): return None if self._current_app is _current_app_undefined else self._current_app + @contextmanager + def bind_template(self, template): + if self.template is not None: + raise RuntimeError("Context is already bound to a template") + self.template = template + try: + yield + finally: + self.template = None + def __copy__(self): duplicate = super(Context, self).__copy__() duplicate.render_context = copy(self.render_context) @@ -210,28 +221,26 @@ def __init__(self, request, dict_=None, processors=None, self._processors_index = len(self.dicts) self.update({}) # placeholder for context processors output - @property - def template(self): - return self._template - - @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 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 processors: - updates.update(processor(self.request)) - self.dicts[self._processors_index] = updates + @contextmanager + def bind_template(self, template): + if self.template is not None: + raise RuntimeError("Context is already bound to a template") + + self.template = template + # Set context processors according to the template engine's settings. + processors = (template.engine.template_context_processors + + self._processors) + updates = {} + for processor in processors: + updates.update(processor(self.request)) + self.dicts[self._processors_index] = updates + + try: + yield + finally: + self.template = None + # Unset context processors. + self.dicts[self._processors_index] = {} def new(self, values=None): new_context = super(RequestContext, self).new(values)