diff --git a/django/template/backends/django.py b/django/template/backends/django.py index af58f60b2a2..cc795aae1cd 100644 --- a/django/template/backends/django.py +++ b/django/template/backends/django.py @@ -23,6 +23,7 @@ class DjangoTemplates(BaseEngine): def __init__(self, params): params = params.copy() options = params.pop('OPTIONS').copy() + options.setdefault('autoescape', True) options.setdefault('debug', settings.DEBUG) options.setdefault('file_charset', settings.FILE_CHARSET) libraries = options.get('libraries', {}) @@ -60,7 +61,7 @@ class Template(object): return self.template.origin def render(self, context=None, request=None): - context = make_context(context, request) + context = make_context(context, request, autoescape=self.backend.engine.autoescape) try: return self.template.render(context) except TemplateDoesNotExist as exc: diff --git a/django/template/context.py b/django/template/context.py index 2643e927f4c..04d0e2f5f42 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -201,9 +201,9 @@ class RequestContext(Context): Additional processors can be specified as a list of callables 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__( - dict_, use_l10n=use_l10n, use_tz=use_tz) + dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape) self.request = request self._processors = () if processors is None else tuple(processors) self._processors_index = len(self.dicts) @@ -245,17 +245,17 @@ class RequestContext(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. """ if request is None: - context = Context(context) + context = Context(context, **kwargs) else: # The following pattern is required to ensure values from # context override those from template context processors. original_context = context - context = RequestContext(request) + context = RequestContext(request, **kwargs) if original_context: context.push(original_context) return context diff --git a/django/template/engine.py b/django/template/engine.py index d439f49679b..b49ed090883 100644 --- a/django/template/engine.py +++ b/django/template/engine.py @@ -18,7 +18,7 @@ class Engine(object): def __init__(self, dirs=None, app_dirs=False, context_processors=None, 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: dirs = [] if context_processors is None: @@ -38,6 +38,7 @@ class Engine(object): self.dirs = dirs self.app_dirs = app_dirs + self.autoescape = autoescape self.context_processors = context_processors self.debug = debug self.loaders = loaders diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 570ffbc9d62..aaa08cc70b8 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -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 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 arguments: @@ -63,6 +63,18 @@ what's passed by :class:`~django.template.backends.django.DjangoTemplates`. 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 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 diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index 1f3857ab28d..6dbe0362b96 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -204,7 +204,9 @@ Signals Templates ^^^^^^^^^ -* ... +* Added the ``autoescape`` option to the + :class:`~django.template.backends.django.DjangoTemplates` backend and the + :class:`~django.template.Engine` class. Tests ^^^^^ diff --git a/docs/topics/templates.txt b/docs/topics/templates.txt index f0abccc8352..4834e9eb06e 100644 --- a/docs/topics/templates.txt +++ b/docs/topics/templates.txt @@ -295,6 +295,19 @@ applications. This generic name was kept for backwards-compatibility. ``DjangoTemplates`` engines accept the following :setting:`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 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 diff --git a/tests/template_backends/test_django.py b/tests/template_backends/test_django.py index 8d13aaee08b..1a93a82274b 100644 --- a/tests/template_backends/test_django.py +++ b/tests/template_backends/test_django.py @@ -1,5 +1,6 @@ from template_tests.test_response import test_processor_name +from django.template import EngineHandler from django.template.backends.django import DjangoTemplates from django.template.library import InvalidTemplateLibrary from django.test import RequestFactory, override_settings @@ -108,3 +109,24 @@ class DjangoTemplatesTests(TemplateStringsTests): '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 & Jim' + )