Removed current_app argument to render() and TemplateResponse().

Per deprecation timeline.
This commit is contained in:
Tim Graham 2015-09-03 15:52:04 -04:00
parent 75374d3797
commit 5e450c52aa
15 changed files with 19 additions and 175 deletions

View File

@ -3,9 +3,6 @@ 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.core import urlresolvers from django.core import urlresolvers
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.db.models.manager import Manager from django.db.models.manager import Manager
@ -14,12 +11,10 @@ from django.http import (
Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect, Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect,
) )
from django.template import RequestContext, loader from django.template import RequestContext, loader
from django.template.context import _current_app_undefined
from django.template.engine import ( from django.template.engine import (
_context_instance_undefined, _dictionary_undefined, _dirs_undefined, _context_instance_undefined, _dictionary_undefined, _dirs_undefined,
) )
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango110Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import Promise from django.utils.functional import Promise
@ -49,7 +44,7 @@ def render_to_response(template_name, context=None,
def render(request, template_name, context=None, def render(request, template_name, context=None,
context_instance=_context_instance_undefined, context_instance=_context_instance_undefined,
content_type=None, status=None, current_app=_current_app_undefined, content_type=None, status=None,
dirs=_dirs_undefined, dictionary=_dictionary_undefined, dirs=_dirs_undefined, dictionary=_dictionary_undefined,
using=None): using=None):
""" """
@ -58,32 +53,18 @@ def render(request, template_name, context=None,
Uses a RequestContext by default. Uses a RequestContext by default.
""" """
if (context_instance is _context_instance_undefined if (context_instance is _context_instance_undefined
and current_app is _current_app_undefined
and dirs is _dirs_undefined and dirs is _dirs_undefined
and dictionary is _dictionary_undefined): and dictionary is _dictionary_undefined):
# No deprecated arguments were passed - use the new code path # No deprecated arguments were passed - use the new code path
# In Django 1.10, request should become a positional argument. # In Django 1.10, request should become a positional argument.
content = loader.render_to_string( content = loader.render_to_string(
template_name, context, request=request, using=using) template_name, context, request=request, using=using)
else: else:
# Some deprecated arguments were passed - use the legacy code path # Some deprecated arguments were passed - use the legacy code path
if context_instance is not _context_instance_undefined: if context_instance is not _context_instance_undefined:
if current_app is not _current_app_undefined: pass
raise ValueError('If you provide a context_instance you must '
'set its current_app before calling render()')
else: else:
context_instance = RequestContext(request) context_instance = RequestContext(request)
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of render is deprecated. "
"Set the current_app attribute of request instead.",
RemovedInDjango110Warning, stacklevel=2)
request.current_app = current_app
# Directly set the private attribute to avoid triggering the
# warning in RequestContext.__init__.
context_instance._current_app = current_app
content = loader.render_to_string( content = loader.render_to_string(
template_name, context, context_instance, dirs, dictionary, template_name, context, context_instance, dirs, dictionary,
using=using) using=using)

View File

@ -1,14 +1,9 @@
import warnings
from contextlib import contextmanager from contextlib import contextmanager
from copy import copy from copy import copy
from django.utils.deprecation import RemovedInDjango110Warning
# Hard-coded processor for easier use of CSRF protection. # Hard-coded processor for easier use of CSRF protection.
_builtin_context_processors = ('django.template.context_processors.csrf',) _builtin_context_processors = ('django.template.context_processors.csrf',)
_current_app_undefined = object()
class ContextPopException(Exception): class ContextPopException(Exception):
"pop() has been called more times than push()" "pop() has been called more times than push()"
@ -135,16 +130,8 @@ class BaseContext(object):
class Context(BaseContext): class Context(BaseContext):
"A stack container for variable context" "A stack container for variable context"
def __init__(self, dict_=None, autoescape=True, def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None):
current_app=_current_app_undefined,
use_l10n=None, use_tz=None):
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of Context is deprecated. Use "
"RequestContext and set the current_app attribute of its "
"request instead.", RemovedInDjango110Warning, stacklevel=2)
self.autoescape = autoescape self.autoescape = autoescape
self._current_app = current_app
self.use_l10n = use_l10n self.use_l10n = use_l10n
self.use_tz = use_tz self.use_tz = use_tz
self.template_name = "unknown" self.template_name = "unknown"
@ -154,14 +141,6 @@ class Context(BaseContext):
self.template = None self.template = None
super(Context, self).__init__(dict_) super(Context, self).__init__(dict_)
@property
def current_app(self):
return None if self._current_app is _current_app_undefined else self._current_app
@property
def is_current_app_set(self):
return self._current_app is not _current_app_undefined
@contextmanager @contextmanager
def bind_template(self, template): def bind_template(self, template):
if self.template is not None: if self.template is not None:
@ -222,19 +201,9 @@ class RequestContext(Context):
Additional processors can be specified as a list of callables Additional processors can be specified as a list of callables
using the "processors" keyword argument. using the "processors" keyword argument.
""" """
def __init__(self, request, dict_=None, processors=None, def __init__(self, request, dict_=None, processors=None, use_l10n=None, use_tz=None):
current_app=_current_app_undefined,
use_l10n=None, use_tz=None):
# current_app isn't passed here to avoid triggering the deprecation
# warning in Context.__init__.
super(RequestContext, self).__init__( super(RequestContext, self).__init__(
dict_, use_l10n=use_l10n, use_tz=use_tz) dict_, use_l10n=use_l10n, use_tz=use_tz)
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of RequestContext is deprecated. "
"Set the current_app attribute of its request instead.",
RemovedInDjango110Warning, stacklevel=2)
self._current_app = current_app
self.request = request self.request = request
self._processors = () if processors is None else tuple(processors) self._processors = () if processors is None else tuple(processors)
self._processors_index = len(self.dicts) self._processors_index = len(self.dicts)

View File

@ -432,23 +432,11 @@ class URLNode(Node):
smart_text(k, 'ascii'): v.resolve(context) smart_text(k, 'ascii'): v.resolve(context)
for k, v in self.kwargs.items() for k, v in self.kwargs.items()
} }
view_name = self.view_name.resolve(context) view_name = self.view_name.resolve(context)
try:
current_app = context.request.current_app
except AttributeError:
# Leave only the else block when the deprecation path for
# Context.current_app completes in Django 1.10.
# Can also remove the Context.is_current_app_set property.
if context.is_current_app_set:
current_app = context.current_app
else:
try: try:
current_app = context.request.resolver_match.namespace current_app = context.request.resolver_match.namespace
except AttributeError: except AttributeError:
current_app = None current_app = None
# Try to look up the URL twice: once given the view name, and again # Try to look up the URL twice: once given the view name, and again
# relative to what we guess is the "main" app. If they both fail, # relative to what we guess is the "main" app. If they both fail,
# re-raise the NoReverseMatch unless we're using the # re-raise the NoReverseMatch unless we're using the

View File

@ -6,7 +6,7 @@ from django.utils.deprecation import RemovedInDjango110Warning
from .backends.django import Template as BackendTemplate from .backends.django import Template as BackendTemplate
from .base import Template from .base import Template
from .context import Context, RequestContext, _current_app_undefined from .context import Context, RequestContext
from .loader import get_template, select_template from .loader import get_template, select_template
@ -190,19 +190,10 @@ class SimpleTemplateResponse(HttpResponse):
class TemplateResponse(SimpleTemplateResponse): class TemplateResponse(SimpleTemplateResponse):
rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request', '_current_app'] rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request']
def __init__(self, request, template, context=None, content_type=None, def __init__(self, request, template, context=None, content_type=None,
status=None, current_app=_current_app_undefined, charset=None, status=None, charset=None, using=None):
using=None):
# As a convenience we'll allow callers to provide current_app without
# having to avoid needing to create the RequestContext directly
if current_app is not _current_app_undefined:
warnings.warn(
"The current_app argument of TemplateResponse is deprecated. "
"Set the current_app attribute of its request instead.",
RemovedInDjango110Warning, stacklevel=2)
request.current_app = current_app
super(TemplateResponse, self).__init__( super(TemplateResponse, self).__init__(
template, context, content_type, status, charset, using) template, context, content_type, status, charset, using)
self._request = request self._request = request

View File

@ -189,7 +189,7 @@ TemplateResponse objects
Methods Methods
------- -------
.. method:: TemplateResponse.__init__(request, template, context=None, content_type=None, status=None, current_app=None, charset=None, using=None) .. method:: TemplateResponse.__init__(request, template, context=None, content_type=None, status=None, charset=None, using=None)
Instantiates a :class:`~django.template.response.TemplateResponse` object Instantiates a :class:`~django.template.response.TemplateResponse` object
with the given request, template, context, content type, HTTP status, and with the given request, template, context, content type, HTTP status, and
@ -224,16 +224,6 @@ Methods
``status`` ``status``
The HTTP status code for the response. The HTTP status code for the response.
``current_app``
A hint indicating which application contains the current view. See the
:ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`
for more information.
.. deprecated:: 1.8
The ``current_app`` argument is deprecated. Instead you should set
``request.current_app``.
``charset`` ``charset``
The charset in which the response will be encoded. If not given it will The charset in which the response will be encoded. If not given it will
be extracted from ``content_type``, and if that is unsuccessful, the be extracted from ``content_type``, and if that is unsuccessful, the

View File

@ -194,7 +194,7 @@ Once you have a compiled :class:`Template` object, you can render a context
with it. You can reuse the same template to render it several times with with it. You can reuse the same template to render it several times with
different contexts. different contexts.
.. class:: Context(dict_=None, current_app=_current_app_undefined) .. class:: Context(dict_=None)
This class lives at ``django.template.Context``. The constructor takes This class lives at ``django.template.Context``. The constructor takes
two optional arguments: two optional arguments:
@ -205,11 +205,6 @@ different contexts.
to help :ref:`resolve namespaced URLs<topics-http-reversing-url-namespaces>`. to help :ref:`resolve namespaced URLs<topics-http-reversing-url-namespaces>`.
If you're not using namespaced URLs, you can ignore this argument. If you're not using namespaced URLs, you can ignore this argument.
.. deprecated:: 1.8
The ``current_app`` argument is deprecated. If you need it, you must
now use a :class:`RequestContext` instead of a :class:`Context`.
For details, see :ref:`playing-with-context` below. For details, see :ref:`playing-with-context` below.
.. method:: Template.render(context) .. method:: Template.render(context)

View File

@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake.
``render`` ``render``
========== ==========
.. function:: render(request, template_name, context=None, context_instance=_context_instance_undefined, content_type=None, status=None, current_app=_current_app_undefined, dirs=_dirs_undefined, using=None) .. function:: render(request, template_name, context=None, context_instance=_context_instance_undefined, content_type=None, status=None, dirs=_dirs_undefined, using=None)
Combines a given template with a given context dictionary and returns an Combines a given template with a given context dictionary and returns an
:class:`~django.http.HttpResponse` object with that rendered text. :class:`~django.http.HttpResponse` object with that rendered text.
@ -67,16 +67,6 @@ Optional arguments
``status`` ``status``
The status code for the response. Defaults to ``200``. The status code for the response. Defaults to ``200``.
``current_app``
A hint indicating which application contains the current view. See the
:ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`
for more information.
.. deprecated:: 1.8
The ``current_app`` argument is deprecated. Instead you should set
``request.current_app``.
``using`` ``using``
The :setting:`NAME <TEMPLATES-NAME>` of a template engine to use for The :setting:`NAME <TEMPLATES-NAME>` of a template engine to use for
loading the template. loading the template.

View File

@ -680,13 +680,6 @@ the fully qualified name into parts and then tries the following lookup:
setting the current application on the :attr:`request.current_app setting the current application on the :attr:`request.current_app
<django.http.HttpRequest.current_app>` attribute. <django.http.HttpRequest.current_app>` attribute.
.. versionchanged:: 1.8
In previous versions of Django, you had to set the ``current_app``
attribute on any :class:`~django.template.Context` or
:class:`~django.template.RequestContext` that is used to render a
template.
.. versionchanged:: 1.9 .. versionchanged:: 1.9
Previously, the :ttag:`url` template tag did not use the namespace of the Previously, the :ttag:`url` template tag did not use the namespace of the

View File

@ -102,19 +102,9 @@ class ShortcutTests(SimpleTestCase):
response = self.client.get('/render/using/?using=jinja2') response = self.client.get('/render/using/?using=jinja2')
self.assertEqual(response.content, b'Jinja2\n') self.assertEqual(response.content, b'Jinja2\n')
@ignore_warnings(category=RemovedInDjango110Warning)
def test_render_with_current_app(self):
response = self.client.get('/render/current_app/')
self.assertEqual(response.context.request.current_app, "foobar_app")
@ignore_warnings(category=RemovedInDjango110Warning) @ignore_warnings(category=RemovedInDjango110Warning)
def test_render_with_dirs(self): def test_render_with_dirs(self):
response = self.client.get('/render/dirs/') response = self.client.get('/render/dirs/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'spam eggs\n') self.assertEqual(response.content, b'spam eggs\n')
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
@ignore_warnings(category=RemovedInDjango110Warning)
def test_render_with_current_app_conflict(self):
with self.assertRaises(ValueError):
self.client.get('/render/current_app_conflict/')

View File

@ -18,6 +18,4 @@ urlpatterns = [
url(r'^render/dirs/$', views.render_with_dirs), url(r'^render/dirs/$', views.render_with_dirs),
url(r'^render/status/$', views.render_view_with_status), url(r'^render/status/$', views.render_view_with_status),
url(r'^render/using/$', views.render_view_with_using), url(r'^render/using/$', views.render_view_with_using),
url(r'^render/current_app/$', views.render_view_with_current_app),
url(r'^render/current_app_conflict/$', views.render_view_with_current_app_conflict),
] ]

View File

@ -109,19 +109,3 @@ def render_view_with_status(request):
def render_view_with_using(request): def render_view_with_using(request):
using = request.GET.get('using') using = request.GET.get('using')
return render(request, 'shortcuts/using.html', using=using) return render(request, 'shortcuts/using.html', using=using)
def render_view_with_current_app(request):
return render(request, 'shortcuts/render_test.html', {
'foo': 'FOO',
'bar': 'BAR',
}, current_app="foobar_app")
def render_view_with_current_app_conflict(request):
# This should fail because we don't passing both a current_app and
# context_instance:
return render(request, 'shortcuts/render_test.html', {
'foo': 'FOO',
'bar': 'BAR',
}, current_app="foobar_app", context_instance=RequestContext(request))

View File

@ -1 +0,0 @@
{% load custom %}{% current_app %}

View File

@ -166,13 +166,6 @@ def inclusion_only_unlimited_args_from_template(*args):
inclusion_only_unlimited_args_from_template.anything = "Expected inclusion_only_unlimited_args_from_template __dict__" inclusion_only_unlimited_args_from_template.anything = "Expected inclusion_only_unlimited_args_from_template __dict__"
@register.inclusion_tag('test_incl_tag_current_app.html', takes_context=True)
def inclusion_tag_current_app(context):
"""Expected inclusion_tag_current_app __doc__"""
return {}
inclusion_tag_current_app.anything = "Expected inclusion_tag_current_app __dict__"
@register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True) @register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True)
def inclusion_tag_use_l10n(context): def inclusion_tag_use_l10n(context):
"""Expected inclusion_tag_use_l10n __doc__""" """Expected inclusion_tag_use_l10n __doc__"""

View File

@ -6,10 +6,9 @@ from unittest import skipUnless
from django.template import Context, Engine, TemplateSyntaxError from django.template import Context, Engine, TemplateSyntaxError
from django.template.base import Node from django.template.base import Node
from django.template.library import InvalidTemplateLibrary from django.template.library import InvalidTemplateLibrary
from django.test import SimpleTestCase, ignore_warnings from django.test import SimpleTestCase
from django.test.utils import extend_sys_path from django.test.utils import extend_sys_path
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango110Warning
from .templatetags import custom, inclusion from .templatetags import custom, inclusion
from .utils import ROOT from .utils import ROOT
@ -270,23 +269,8 @@ class InclusionTagTests(TagTestCase):
self.verify_tag(inclusion.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args') self.verify_tag(inclusion.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args')
self.verify_tag(inclusion.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter') self.verify_tag(inclusion.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter')
self.verify_tag(inclusion.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n') self.verify_tag(inclusion.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n')
self.verify_tag(inclusion.inclusion_tag_current_app, 'inclusion_tag_current_app')
self.verify_tag(inclusion.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs') self.verify_tag(inclusion.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs')
@ignore_warnings(category=RemovedInDjango110Warning)
def test_15070_current_app(self):
"""
Test that inclusion tag passes down `current_app` of context to the
Context of the included/rendered template as well.
"""
c = Context({})
t = self.engine.from_string('{% load inclusion %}{% inclusion_tag_current_app %}')
self.assertEqual(t.render(c).strip(), 'None')
# That part produces the deprecation warning
c = Context({}, current_app='advanced')
self.assertEqual(t.render(c).strip(), 'advanced')
def test_15070_use_l10n(self): def test_15070_use_l10n(self):
""" """
Test that inclusion tag passes down `use_l10n` of context to the Test that inclusion tag passes down `use_l10n` of context to the

View File

@ -282,11 +282,6 @@ class TemplateResponseTest(SimpleTestCase):
response = TemplateResponse(request, 'template_tests/using.html', using='jinja2').render() response = TemplateResponse(request, 'template_tests/using.html', using='jinja2').render()
self.assertEqual(response.content, b'Jinja2\n') self.assertEqual(response.content, b'Jinja2\n')
@ignore_warnings(category=RemovedInDjango110Warning)
def test_custom_app(self):
self._response('{{ foo }}', current_app="foobar")
self.assertEqual(self._request.current_app, 'foobar')
def test_pickling(self): def test_pickling(self):
# Create a template response. The context is # Create a template response. The context is
# known to be unpickleable (e.g., a function). # known to be unpickleable (e.g., a function).
@ -310,8 +305,12 @@ class TemplateResponseTest(SimpleTestCase):
# ...and the unpickled response doesn't have the # ...and the unpickled response doesn't have the
# template-related attributes, so it can't be re-rendered # template-related attributes, so it can't be re-rendered
template_attrs = ('template_name', 'context_data', template_attrs = (
'_post_render_callbacks', '_request', '_current_app') 'template_name',
'context_data',
'_post_render_callbacks',
'_request',
)
for attr in template_attrs: for attr in template_attrs:
self.assertFalse(hasattr(unpickled_response, attr)) self.assertFalse(hasattr(unpickled_response, attr))