summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2014-11-28 23:50:34 +0100
committerAymeric Augustin <aymeric.augustin@m4x.org>2014-12-28 16:23:02 +0100
commit90805b240fdb1a1570c414f7d55fa1b0b77e1f24 (patch)
tree4f3fcccb3b8709c54eb2c7a1e00967694e25390f
parentf9a6ebf6f5c8e1723db680bf6d462a9df8889d7f (diff)
Supported multiple template engines in render_to_string.
Adjusted its API through a deprecation path according to the DEP.
-rw-r--r--django/shortcuts.py18
-rw-r--r--django/template/engine.py37
-rw-r--r--django/template/loader.py60
-rw-r--r--docs/internals/deprecation.txt5
-rw-r--r--docs/ref/templates/api.txt17
-rw-r--r--docs/releases/1.8.txt14
-rw-r--r--tests/template_tests/test_loaders.py22
7 files changed, 146 insertions, 27 deletions
diff --git a/django/shortcuts.py b/django/shortcuts.py
index 62560472c1..d1c0770af9 100644
--- a/django/shortcuts.py
+++ b/django/shortcuts.py
@@ -3,6 +3,8 @@ This module collects helper functions and classes that "span" multiple levels
of MVC. In other words, these functions/classes introduce controlled coupling
for convenience's sake.
"""
+import warnings
+
from django.template import loader, RequestContext
from django.http import HttpResponse, Http404
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
@@ -11,6 +13,7 @@ from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from django.core import urlresolvers
from django.utils import six
+from django.utils.deprecation import RemovedInDjango20Warning
def render_to_response(*args, **kwargs):
@@ -20,7 +23,12 @@ def render_to_response(*args, **kwargs):
"""
httpresponse_kwargs = {'content_type': kwargs.pop('content_type', None)}
- return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
+ # TODO: refactor to avoid the deprecated code path.
+ with warnings.catch_warnings():
+ warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
+ content = loader.render_to_string(*args, **kwargs)
+
+ return HttpResponse(content, **httpresponse_kwargs)
def render(request, *args, **kwargs):
@@ -45,8 +53,12 @@ def render(request, *args, **kwargs):
kwargs['context_instance'] = context_instance
- return HttpResponse(loader.render_to_string(*args, **kwargs),
- **httpresponse_kwargs)
+ # TODO: refactor to avoid the deprecated code path.
+ with warnings.catch_warnings():
+ warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
+ content = loader.render_to_string(*args, **kwargs)
+
+ return HttpResponse(content, **httpresponse_kwargs)
def redirect(to, *args, **kwargs):
diff --git a/django/template/engine.py b/django/template/engine.py
index 3bf953ce7c..a76d52c565 100644
--- a/django/template/engine.py
+++ b/django/template/engine.py
@@ -12,6 +12,8 @@ from .base import Context, Lexer, Parser, Template, TemplateDoesNotExist
from .context import _builtin_context_processors
+_context_instance_undefined = object()
+_dictionary_undefined = object()
_dirs_undefined = object()
@@ -165,14 +167,22 @@ class Engine(object):
template = Template(template, origin, template_name, engine=self)
return template
- def render_to_string(self, template_name, dictionary=None, context_instance=None,
- dirs=_dirs_undefined):
+ def render_to_string(self, template_name, context=None,
+ context_instance=_context_instance_undefined,
+ dirs=_dirs_undefined,
+ dictionary=_dictionary_undefined):
"""
Loads the given template_name and renders it with the given dictionary as
context. The template_name may be a string to load a single template using
get_template, or it may be a tuple to use select_template to find one of
the templates in the list. Returns a string.
"""
+ if context_instance is _context_instance_undefined:
+ context_instance = None
+ else:
+ warnings.warn(
+ "The context_instance argument of render_to_string is "
+ "deprecated.", RemovedInDjango20Warning, stacklevel=2)
if dirs is _dirs_undefined:
# Do not set dirs to None here to avoid triggering the deprecation
# warning in select_template or get_template.
@@ -181,23 +191,30 @@ class Engine(object):
warnings.warn(
"The dirs argument of render_to_string is deprecated.",
RemovedInDjango20Warning, stacklevel=2)
+ if dictionary is _dictionary_undefined:
+ dictionary = None
+ else:
+ warnings.warn(
+ "The dictionary argument of render_to_string was renamed to "
+ "context.", RemovedInDjango20Warning, stacklevel=2)
+ context = dictionary
if isinstance(template_name, (list, tuple)):
t = self.select_template(template_name, dirs)
else:
t = self.get_template(template_name, dirs)
if not context_instance:
- # Django < 1.8 accepted a Context in `dictionary` even though that's
- # unintended. Preserve this ability but don't rewrap `dictionary`.
- if isinstance(dictionary, Context):
- return t.render(dictionary)
+ # Django < 1.8 accepted a Context in `context` even though that's
+ # unintended. Preserve this ability but don't rewrap `context`.
+ if isinstance(context, Context):
+ return t.render(context)
else:
- return t.render(Context(dictionary))
- if not dictionary:
+ return t.render(Context(context))
+ if not context:
return t.render(context_instance)
- # Add the dictionary to the context stack, ensuring it gets removed again
+ # Add the context to the context stack, ensuring it gets removed again
# to keep the context_instance in the same state it started in.
- with context_instance.push(dictionary):
+ with context_instance.push(context):
return t.render(context_instance)
def select_template(self, template_name_list, dirs=_dirs_undefined):
diff --git a/django/template/loader.py b/django/template/loader.py
index 87a9789b31..0bc64e4d7d 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -5,7 +5,8 @@ from django.utils.deprecation import RemovedInDjango20Warning
from . import engines
from .backends.django import DjangoTemplates
from .base import Origin, TemplateDoesNotExist
-from .engine import _dirs_undefined, Engine
+from .engine import (
+ _context_instance_undefined, _dictionary_undefined, _dirs_undefined)
class LoaderOrigin(Origin):
@@ -75,8 +76,61 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
raise TemplateDoesNotExist("No template names provided")
-def render_to_string(*args, **kwargs):
- return Engine.get_default().render_to_string(*args, **kwargs)
+def render_to_string(template_name, context=None,
+ context_instance=_context_instance_undefined,
+ dirs=_dirs_undefined,
+ dictionary=_dictionary_undefined,
+ using=None):
+ """
+ Loads a template and renders it with a context. Returns a string.
+
+ template_name may be a string or a list of strings.
+ """
+ if (context_instance is _context_instance_undefined
+ and dirs is _dirs_undefined
+ and dictionary is _dictionary_undefined):
+ # No deprecated arguments were passed - use the new code path
+ if isinstance(template_name, (list, tuple)):
+ template = select_template(template_name, using=using)
+ else:
+ template = get_template(template_name, using=using)
+ return template.render(context)
+
+ else:
+ # Some deprecated arguments were passed - use the legacy code path
+ for engine in _engine_list(using):
+ try:
+ # This is required for deprecating arguments specific to Django
+ # templates. Simply return engine.render_to_string(template_name,
+ # context) in Django 2.0.
+ if isinstance(engine, DjangoTemplates):
+ # Hack -- use the internal Engine instance of DjangoTemplates.
+ return engine.engine.render_to_string(
+ template_name, context, context_instance, dirs, dictionary)
+ elif context_instance is not _context_instance_undefined:
+ warnings.warn(
+ "Skipping template backend %s because its render_to_string "
+ "method doesn't support the context_instance argument." %
+ engine.name, stacklevel=2)
+ elif dirs is not _dirs_undefined:
+ warnings.warn(
+ "Skipping template backend %s because its render_to_string "
+ "method doesn't support the dirs argument." % engine.name,
+ stacklevel=2)
+ elif dictionary is not _dictionary_undefined:
+ warnings.warn(
+ "Skipping template backend %s because its render_to_string "
+ "method doesn't support the dictionary argument." %
+ engine.name, stacklevel=2)
+ except TemplateDoesNotExist:
+ continue
+
+ if template_name:
+ if isinstance(template_name, (list, tuple)):
+ template_name = ', '.join(template_name)
+ raise TemplateDoesNotExist(template_name)
+ else:
+ raise TemplateDoesNotExist("No template names provided")
def _engine_list(using=None):
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 50ff5ce3b9..216aab9574 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -88,6 +88,11 @@ details on these changes.
* The backwards compatibility alias ``django.template.loader.BaseLoader`` will
be removed.
+* The ``dictionary`` and ``context_instance`` parameters for the following
+ functions will be removed:
+
+ * ``django.template.loader.render_to_string()``
+
* The ``dirs`` parameter for the following functions will be removed:
* ``django.template.loader.get_template()``
diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt
index c86b7873ae..f184544838 100644
--- a/docs/ref/templates/api.txt
+++ b/docs/ref/templates/api.txt
@@ -890,7 +890,7 @@ When :setting:`TEMPLATE_DEBUG` is ``True`` template objects will have an
The ``render_to_string`` shortcut
===================================
-.. function:: loader.render_to_string(template_name, dictionary=None, context_instance=None)
+.. function:: loader.render_to_string(template_name, context=None, context_instance=None)
To cut down on the repetitive nature of loading and rendering
templates, Django provides a shortcut function which largely
@@ -906,16 +906,25 @@ The ``render_to_string`` shortcut takes one required argument --
and render (or a list of template names, in which case Django will use
the first template in the list that exists) -- and two optional arguments:
-dictionary
+``context``
A dictionary to be used as variables and values for the
- template's context. This can also be passed as the second
+ template's context. This should be passed as the second
positional argument.
-context_instance
+ .. versionchanged:: 1.8
+
+ The ``context`` argument used to be called ``dictionary``. That name
+ is deprecated in Django 1.8 and will be removed in Django 2.0.
+
+``context_instance``
An instance of :class:`~django.template.Context` or a subclass (e.g., an
instance of :class:`~django.template.RequestContext`) to use as the
template's context. This can also be passed as the third positional argument.
+ .. deprecated:: 1.8
+
+ The ``context_instance`` argument is deprecated. Simply use ``context``.
+
See also the :func:`~django.shortcuts.render_to_response()` shortcut, which
calls ``render_to_string`` and feeds the result into an :class:`~django.http.HttpResponse`
suitable for returning directly from a view.
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 8bf3659ba2..54ff4c605e 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -1198,6 +1198,20 @@ to construct the "view on site" URL. This URL is now accessible using the
sure to provide a default value for the ``form_class`` argument since it's
now optional.
+``dictionary`` and ``context_instance`` arguments of rendering functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following functions will no longer accept the ``dictionary`` and
+``context_instance`` parameters in Django 2.0:
+
+* ``django.template.loader.render_to_string()``
+
+Use the ``context`` parameter instead. When ``dictionary`` is passed as a
+positional argument, which is the most common idiom, no changes are needed.
+
+There is no replacement for ``context_instance``. All data must be passed to
+templates through the ``context`` dict.
+
``dirs`` argument of template-finding functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/template_tests/test_loaders.py b/tests/template_tests/test_loaders.py
index f068d8b71a..14390c3f46 100644
--- a/tests/template_tests/test_loaders.py
+++ b/tests/template_tests/test_loaders.py
@@ -153,13 +153,6 @@ class RenderToStringTest(SimpleTestCase):
self.assertEqual(loader.render_to_string('test_context.html',
{'obj': 'test'}), 'obj:test\n')
- def test_existing_context_kept_clean(self):
- context = Context({'obj': 'before'})
- output = loader.render_to_string('test_context.html', {'obj': 'after'},
- context_instance=context)
- self.assertEqual(output, 'obj:after\n')
- self.assertEqual(context['obj'], 'before')
-
def test_empty_list(self):
six.assertRaisesRegex(self, TemplateDoesNotExist,
'No template names provided$',
@@ -170,6 +163,21 @@ class RenderToStringTest(SimpleTestCase):
'No template names provided$',
loader.select_template, [])
+
+@override_settings(
+ TEMPLATE_DIRS=(
+ os.path.join(os.path.dirname(upath(__file__)), 'templates'),
+ )
+)
+class DeprecatedRenderToStringTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase):
+
+ def test_existing_context_kept_clean(self):
+ context = Context({'obj': 'before'})
+ output = loader.render_to_string('test_context.html', {'obj': 'after'},
+ context_instance=context)
+ self.assertEqual(output, 'obj:after\n')
+ self.assertEqual(context['obj'], 'before')
+
def test_no_empty_dict_pushed_to_stack(self):
"""
No empty dict should be pushed to the context stack when render_to_string