From 37505b6397058bcc3460f23d48a7de9641cd6ef0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Thu, 20 Nov 2014 22:34:59 +0100 Subject: [PATCH] Encapsulated TEMPLATE_CONTEXT_PROCESSORS in Engine. Since RequestContext doesn't know its Engine until it's passed to Template.render() -- and cannot without breaking a widely used public API -- an elaborate hack is required to apply context processors. --- django/template/context.py | 48 ++++++++++++++++++++++++-------------- django/template/engine.py | 7 ++++++ django/test/signals.py | 7 ------ 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/django/template/context.py b/django/template/context.py index e48cfaef3b..cf59789a6f 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -1,8 +1,5 @@ from copy import copy -from django.conf import settings -from django.utils import lru_cache -from django.utils.module_loading import import_string # Hard-coded processor for easier use of CSRF protection. _builtin_context_processors = ('django.core.context_processors.csrf',) @@ -172,13 +169,6 @@ class RenderContext(BaseContext): return self.dicts[-1][key] -@lru_cache.lru_cache() -def get_standard_processors(): - context_processors = _builtin_context_processors - context_processors += tuple(settings.TEMPLATE_CONTEXT_PROCESSORS) - return tuple(import_string(path) for path in context_processors) - - class RequestContext(Context): """ This subclass of template.Context automatically populates itself using @@ -190,11 +180,33 @@ class RequestContext(Context): use_l10n=None, use_tz=None, engine=None): Context.__init__(self, dict_, current_app=current_app, use_l10n=use_l10n, use_tz=use_tz, engine=engine) - if processors is None: - processors = () - else: - processors = tuple(processors) - updates = dict() - for processor in get_standard_processors() + processors: - updates.update(processor(request)) - self.update(updates) + self._request = request + 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 + + @engine.setter + def engine(self, engine): + self._engine = engine + if hasattr(self, '_processors_index'): + if engine is None: + # Unset context processors. + self.dicts[self._processors_index] = {} + else: + # Set context processors for this engine. + updates = {} + for processor in engine.template_context_processors + self._processors: + updates.update(processor(self._request)) + self.dicts[self._processors_index] = updates + + def new(self, values=None): + new_context = super(RequestContext, self).new(values) + # This is for backwards-compatibility: RequestContexts created via + # Context.new don't include values from context processors. + del new_context._processors_index + return new_context diff --git a/django/template/engine.py b/django/template/engine.py index 378fb4b20c..ce497ad234 100644 --- a/django/template/engine.py +++ b/django/template/engine.py @@ -9,6 +9,7 @@ from django.utils.functional import cached_property from django.utils.module_loading import import_string from .base import Context, Lexer, Parser, Template, TemplateDoesNotExist +from .context import _builtin_context_processors _dirs_undefined = object() @@ -58,6 +59,12 @@ class Engine(object): file_charset=settings.FILE_CHARSET, ) + @cached_property + def template_context_processors(self): + context_processors = _builtin_context_processors + context_processors += tuple(self.context_processors) + return tuple(import_string(path) for path in context_processors) + @cached_property def template_loaders(self): return self.get_template_loaders(self.loaders) diff --git a/django/test/signals.py b/django/test/signals.py index 2da973fcec..558f6cef21 100644 --- a/django/test/signals.py +++ b/django/test/signals.py @@ -91,13 +91,6 @@ def reset_default_template_engine(**kwargs): Engine.get_default.cache_clear() -@receiver(setting_changed) -def clear_context_processors_cache(**kwargs): - if kwargs['setting'] == 'TEMPLATE_CONTEXT_PROCESSORS': - from django.template.context import get_standard_processors - get_standard_processors.cache_clear() - - @receiver(setting_changed) def clear_serializers_cache(**kwargs): if kwargs['setting'] == 'SERIALIZATION_MODULES':