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.
This commit is contained in:
Aymeric Augustin 2014-11-20 22:34:59 +01:00
parent 98ac69af53
commit 37505b6397
3 changed files with 37 additions and 25 deletions

View File

@ -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

View File

@ -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)

View File

@ -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':