Supported multiple template engines in render_to_string.
Adjusted its API through a deprecation path according to the DEP.
This commit is contained in:
parent
f9a6ebf6f5
commit
90805b240f
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()``
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue