Browse Source

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

Thanks Aymeric for the initial patch and Carl for review.
Aaron Elliot Ross 9 years ago
parent
commit
19a5f6da32

+ 2 - 1
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:

+ 5 - 5
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

+ 2 - 1
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

+ 13 - 1
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

+ 3 - 1
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
 ^^^^^

+ 13 - 0
docs/topics/templates.txt

@@ -295,6 +295,19 @@ applications. This generic name was kept for backwards-compatibility.
 ``DjangoTemplates`` engines accept the following :setting:`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
   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

+ 22 - 0
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 &amp; Jim'
+        )