mirror of https://github.com/django/django.git
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
|
of MVC. In other words, these functions/classes introduce controlled coupling
|
||||||
for convenience's sake.
|
for convenience's sake.
|
||||||
"""
|
"""
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.template import loader, RequestContext
|
from django.template import loader, RequestContext
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
|
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.db.models.query import QuerySet
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
|
|
||||||
|
|
||||||
def render_to_response(*args, **kwargs):
|
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)}
|
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):
|
def render(request, *args, **kwargs):
|
||||||
|
@ -45,8 +53,12 @@ def render(request, *args, **kwargs):
|
||||||
|
|
||||||
kwargs['context_instance'] = context_instance
|
kwargs['context_instance'] = context_instance
|
||||||
|
|
||||||
return HttpResponse(loader.render_to_string(*args, **kwargs),
|
# TODO: refactor to avoid the deprecated code path.
|
||||||
**httpresponse_kwargs)
|
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):
|
def redirect(to, *args, **kwargs):
|
||||||
|
|
|
@ -12,6 +12,8 @@ from .base import Context, Lexer, Parser, Template, TemplateDoesNotExist
|
||||||
from .context import _builtin_context_processors
|
from .context import _builtin_context_processors
|
||||||
|
|
||||||
|
|
||||||
|
_context_instance_undefined = object()
|
||||||
|
_dictionary_undefined = object()
|
||||||
_dirs_undefined = object()
|
_dirs_undefined = object()
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,14 +167,22 @@ class Engine(object):
|
||||||
template = Template(template, origin, template_name, engine=self)
|
template = Template(template, origin, template_name, engine=self)
|
||||||
return template
|
return template
|
||||||
|
|
||||||
def render_to_string(self, template_name, dictionary=None, context_instance=None,
|
def render_to_string(self, template_name, context=None,
|
||||||
dirs=_dirs_undefined):
|
context_instance=_context_instance_undefined,
|
||||||
|
dirs=_dirs_undefined,
|
||||||
|
dictionary=_dictionary_undefined):
|
||||||
"""
|
"""
|
||||||
Loads the given template_name and renders it with the given dictionary as
|
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
|
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
|
get_template, or it may be a tuple to use select_template to find one of
|
||||||
the templates in the list. Returns a string.
|
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:
|
if dirs is _dirs_undefined:
|
||||||
# Do not set dirs to None here to avoid triggering the deprecation
|
# Do not set dirs to None here to avoid triggering the deprecation
|
||||||
# warning in select_template or get_template.
|
# warning in select_template or get_template.
|
||||||
|
@ -181,23 +191,30 @@ class Engine(object):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"The dirs argument of render_to_string is deprecated.",
|
"The dirs argument of render_to_string is deprecated.",
|
||||||
RemovedInDjango20Warning, stacklevel=2)
|
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)):
|
if isinstance(template_name, (list, tuple)):
|
||||||
t = self.select_template(template_name, dirs)
|
t = self.select_template(template_name, dirs)
|
||||||
else:
|
else:
|
||||||
t = self.get_template(template_name, dirs)
|
t = self.get_template(template_name, dirs)
|
||||||
if not context_instance:
|
if not context_instance:
|
||||||
# Django < 1.8 accepted a Context in `dictionary` even though that's
|
# Django < 1.8 accepted a Context in `context` even though that's
|
||||||
# unintended. Preserve this ability but don't rewrap `dictionary`.
|
# unintended. Preserve this ability but don't rewrap `context`.
|
||||||
if isinstance(dictionary, Context):
|
if isinstance(context, Context):
|
||||||
return t.render(dictionary)
|
return t.render(context)
|
||||||
else:
|
else:
|
||||||
return t.render(Context(dictionary))
|
return t.render(Context(context))
|
||||||
if not dictionary:
|
if not context:
|
||||||
return t.render(context_instance)
|
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.
|
# 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)
|
return t.render(context_instance)
|
||||||
|
|
||||||
def select_template(self, template_name_list, dirs=_dirs_undefined):
|
def select_template(self, template_name_list, dirs=_dirs_undefined):
|
||||||
|
|
|
@ -5,7 +5,8 @@ from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from . import engines
|
from . import engines
|
||||||
from .backends.django import DjangoTemplates
|
from .backends.django import DjangoTemplates
|
||||||
from .base import Origin, TemplateDoesNotExist
|
from .base import Origin, TemplateDoesNotExist
|
||||||
from .engine import _dirs_undefined, Engine
|
from .engine import (
|
||||||
|
_context_instance_undefined, _dictionary_undefined, _dirs_undefined)
|
||||||
|
|
||||||
|
|
||||||
class LoaderOrigin(Origin):
|
class LoaderOrigin(Origin):
|
||||||
|
@ -75,8 +76,61 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
|
||||||
raise TemplateDoesNotExist("No template names provided")
|
raise TemplateDoesNotExist("No template names provided")
|
||||||
|
|
||||||
|
|
||||||
def render_to_string(*args, **kwargs):
|
def render_to_string(template_name, context=None,
|
||||||
return Engine.get_default().render_to_string(*args, **kwargs)
|
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):
|
def _engine_list(using=None):
|
||||||
|
|
|
@ -88,6 +88,11 @@ details on these changes.
|
||||||
* The backwards compatibility alias ``django.template.loader.BaseLoader`` will
|
* The backwards compatibility alias ``django.template.loader.BaseLoader`` will
|
||||||
be removed.
|
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:
|
* The ``dirs`` parameter for the following functions will be removed:
|
||||||
|
|
||||||
* ``django.template.loader.get_template()``
|
* ``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
|
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
|
To cut down on the repetitive nature of loading and rendering
|
||||||
templates, Django provides a shortcut function which largely
|
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
|
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:
|
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
|
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.
|
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
|
An instance of :class:`~django.template.Context` or a subclass (e.g., an
|
||||||
instance of :class:`~django.template.RequestContext`) to use as the
|
instance of :class:`~django.template.RequestContext`) to use as the
|
||||||
template's context. This can also be passed as the third positional argument.
|
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
|
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`
|
calls ``render_to_string`` and feeds the result into an :class:`~django.http.HttpResponse`
|
||||||
suitable for returning directly from a view.
|
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
|
sure to provide a default value for the ``form_class`` argument since it's
|
||||||
now optional.
|
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
|
``dirs`` argument of template-finding functions
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -153,13 +153,6 @@ class RenderToStringTest(SimpleTestCase):
|
||||||
self.assertEqual(loader.render_to_string('test_context.html',
|
self.assertEqual(loader.render_to_string('test_context.html',
|
||||||
{'obj': 'test'}), 'obj:test\n')
|
{'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):
|
def test_empty_list(self):
|
||||||
six.assertRaisesRegex(self, TemplateDoesNotExist,
|
six.assertRaisesRegex(self, TemplateDoesNotExist,
|
||||||
'No template names provided$',
|
'No template names provided$',
|
||||||
|
@ -170,6 +163,21 @@ class RenderToStringTest(SimpleTestCase):
|
||||||
'No template names provided$',
|
'No template names provided$',
|
||||||
loader.select_template, [])
|
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):
|
def test_no_empty_dict_pushed_to_stack(self):
|
||||||
"""
|
"""
|
||||||
No empty dict should be pushed to the context stack when render_to_string
|
No empty dict should be pushed to the context stack when render_to_string
|
||||||
|
|
Loading…
Reference in New Issue