Fixed #25469 -- Added autoescape option to DjangoTemplates backend.

Thanks Aymeric for the initial patch and Carl for review.
This commit is contained in:
Aaron Elliot Ross 2015-11-08 10:06:07 +01:00 committed by Tim Graham
parent a8f05f405f
commit 19a5f6da32
7 changed files with 60 additions and 9 deletions

View File

@ -23,6 +23,7 @@ class DjangoTemplates(BaseEngine):
def __init__(self, params): def __init__(self, params):
params = params.copy() params = params.copy()
options = params.pop('OPTIONS').copy() options = params.pop('OPTIONS').copy()
options.setdefault('autoescape', True)
options.setdefault('debug', settings.DEBUG) options.setdefault('debug', settings.DEBUG)
options.setdefault('file_charset', settings.FILE_CHARSET) options.setdefault('file_charset', settings.FILE_CHARSET)
libraries = options.get('libraries', {}) libraries = options.get('libraries', {})
@ -60,7 +61,7 @@ class Template(object):
return self.template.origin return self.template.origin
def render(self, context=None, request=None): def render(self, context=None, request=None):
context = make_context(context, request) context = make_context(context, request, autoescape=self.backend.engine.autoescape)
try: try:
return self.template.render(context) return self.template.render(context)
except TemplateDoesNotExist as exc: except TemplateDoesNotExist as exc:

View File

@ -201,9 +201,9 @@ class RequestContext(Context):
Additional processors can be specified as a list of callables Additional processors can be specified as a list of callables
using the "processors" keyword argument. using the "processors" keyword argument.
""" """
def __init__(self, request, dict_=None, processors=None, use_l10n=None, use_tz=None): def __init__(self, request, dict_=None, processors=None, use_l10n=None, use_tz=None, autoescape=True):
super(RequestContext, self).__init__( super(RequestContext, self).__init__(
dict_, use_l10n=use_l10n, use_tz=use_tz) dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape)
self.request = request self.request = request
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)
@ -245,17 +245,17 @@ class RequestContext(Context):
return new_context return new_context
def make_context(context, request=None): def make_context(context, request=None, **kwargs):
""" """
Create a suitable Context from a plain dict and optionally an HttpRequest. Create a suitable Context from a plain dict and optionally an HttpRequest.
""" """
if request is None: if request is None:
context = Context(context) context = Context(context, **kwargs)
else: else:
# The following pattern is required to ensure values from # The following pattern is required to ensure values from
# context override those from template context processors. # context override those from template context processors.
original_context = context original_context = context
context = RequestContext(request) context = RequestContext(request, **kwargs)
if original_context: if original_context:
context.push(original_context) context.push(original_context)
return context return context

View File

@ -18,7 +18,7 @@ class Engine(object):
def __init__(self, dirs=None, app_dirs=False, context_processors=None, def __init__(self, dirs=None, app_dirs=False, context_processors=None,
debug=False, loaders=None, string_if_invalid='', debug=False, loaders=None, string_if_invalid='',
file_charset='utf-8', libraries=None, builtins=None): file_charset='utf-8', libraries=None, builtins=None, autoescape=True):
if dirs is None: if dirs is None:
dirs = [] dirs = []
if context_processors is None: if context_processors is None:
@ -38,6 +38,7 @@ class Engine(object):
self.dirs = dirs self.dirs = dirs
self.app_dirs = app_dirs self.app_dirs = app_dirs
self.autoescape = autoescape
self.context_processors = context_processors self.context_processors = context_processors
self.debug = debug self.debug = debug
self.loaders = loaders self.loaders = loaders

View File

@ -48,7 +48,7 @@ probably isn't the documentation you're looking for. An instance of the
of that backend and any attribute defaults mentioned below are overridden by of that backend and any attribute defaults mentioned below are overridden by
what's passed by :class:`~django.template.backends.django.DjangoTemplates`. what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
.. class:: Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None) .. class:: Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True)
When instantiating an ``Engine`` all arguments must be passed as keyword When instantiating an ``Engine`` all arguments must be passed as keyword
arguments: arguments:
@ -63,6 +63,18 @@ what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
It defaults to ``False``. It defaults to ``False``.
* ``autoescape`` controls whether HTML autoescaping is enabled.
It defaults to ``True``.
.. warning::
Only set it to ``False`` if you're rendering non-HTML templates!
.. versionadded:: 1.10
The ``autoescape`` option was added.
* ``context_processors`` is a list of dotted Python paths to callables * ``context_processors`` is a list of dotted Python paths to callables
that are used to populate the context when a template is rendered with a that are used to populate the context when a template is rendered with a
request. These callables take a request object as their argument and request. These callables take a request object as their argument and

View File

@ -204,7 +204,9 @@ Signals
Templates Templates
^^^^^^^^^ ^^^^^^^^^
* ... * Added the ``autoescape`` option to the
:class:`~django.template.backends.django.DjangoTemplates` backend and the
:class:`~django.template.Engine` class.
Tests Tests
^^^^^ ^^^^^

View File

@ -295,6 +295,19 @@ applications. This generic name was kept for backwards-compatibility.
``DjangoTemplates`` engines accept the following :setting:`OPTIONS ``DjangoTemplates`` engines accept the following :setting:`OPTIONS
<TEMPLATES-OPTIONS>`: <TEMPLATES-OPTIONS>`:
* ``'autoescape'``: a boolean that controls whether HTML autoescaping is
enabled.
It defaults to ``True``.
.. warning::
Only set it to ``False`` if you're rendering non-HTML templates!
.. versionadded:: 1.10
The ``autoescape`` option was added.
* ``'context_processors'``: a list of dotted Python paths to callables that * ``'context_processors'``: a list of dotted Python paths to callables that
are used to populate the context when a template is rendered with a request. are used to populate the context when a template is rendered with a request.
These callables take a request object as their argument and return a These callables take a request object as their argument and return a

View File

@ -1,5 +1,6 @@
from template_tests.test_response import test_processor_name from template_tests.test_response import test_processor_name
from django.template import EngineHandler
from django.template.backends.django import DjangoTemplates from django.template.backends.django import DjangoTemplates
from django.template.library import InvalidTemplateLibrary from django.template.library import InvalidTemplateLibrary
from django.test import RequestFactory, override_settings from django.test import RequestFactory, override_settings
@ -108,3 +109,24 @@ class DjangoTemplatesTests(TemplateStringsTests):
'template_backends.apps.good.templatetags.good_tags', 'template_backends.apps.good.templatetags.good_tags',
] ]
) )
def test_autoescape_off(self):
templates = [{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {'autoescape': False},
}]
engines = EngineHandler(templates=templates)
self.assertEqual(
engines['django'].from_string('Hello, {{ name }}').render({'name': 'Bob & Jim'}),
'Hello, Bob & Jim'
)
def test_autoescape_default(self):
templates = [{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
}]
engines = EngineHandler(templates=templates)
self.assertEqual(
engines['django'].from_string('Hello, {{ name }}').render({'name': 'Bob & Jim'}),
'Hello, Bob &amp; Jim'
)