Deprecated some arguments of django.shortcuts.render(_to_response).

dictionary and context_instance and superseded by context.

Refactored tests that relied context_instance with more modern idioms.
This commit is contained in:
Aymeric Augustin 2014-12-14 10:17:18 +01:00
parent a0141f9eac
commit fdbfc98003
12 changed files with 137 additions and 92 deletions

View File

@ -65,6 +65,10 @@ class PermWrapperTests(TestCase):
TEMPLATE_DIRS=( TEMPLATE_DIRS=(
os.path.join(os.path.dirname(upath(__file__)), 'templates'), os.path.join(os.path.dirname(upath(__file__)), 'templates'),
), ),
TEMPLATE_CONTEXT_PROCESSORS=(
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages'
),
ROOT_URLCONF='django.contrib.auth.tests.urls', ROOT_URLCONF='django.contrib.auth.tests.urls',
USE_TZ=False, # required for loading the fixture USE_TZ=False, # required for loading the fixture
PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
@ -80,9 +84,6 @@ class AuthContextProcessorTests(TestCase):
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
), ),
TEMPLATE_CONTEXT_PROCESSORS=(
'django.contrib.auth.context_processors.auth',
),
) )
def test_session_not_accessed(self): def test_session_not_accessed(self):
""" """
@ -97,9 +98,6 @@ class AuthContextProcessorTests(TestCase):
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
), ),
TEMPLATE_CONTEXT_PROCESSORS=(
'django.contrib.auth.context_processors.auth',
),
) )
def test_session_is_accessed(self): def test_session_is_accessed(self):
""" """

View File

@ -1,13 +1,12 @@
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import context_processors
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.urls import urlpatterns from django.contrib.auth.urls import urlpatterns
from django.contrib.auth import views from django.contrib.auth import views
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.messages.api import info from django.contrib.messages.api import info
from django.http import HttpResponse, HttpRequest from django.http import HttpResponse, HttpRequest
from django.shortcuts import render_to_response from django.shortcuts import render
from django.template import Template, RequestContext from django.template import Template, RequestContext
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
@ -27,39 +26,35 @@ def remote_user_auth_view(request):
def auth_processor_no_attr_access(request): def auth_processor_no_attr_access(request):
render_to_response('context_processors/auth_attrs_no_access.html', render(request, 'context_processors/auth_attrs_no_access.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth]))
# *After* rendering, we check whether the session was accessed # *After* rendering, we check whether the session was accessed
return render_to_response('context_processors/auth_attrs_test_access.html', return render(request,
{'session_accessed': request.session.accessed}) 'context_processors/auth_attrs_test_access.html',
{'session_accessed': request.session.accessed})
def auth_processor_attr_access(request): def auth_processor_attr_access(request):
render_to_response('context_processors/auth_attrs_access.html', render(request, 'context_processors/auth_attrs_access.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth])) return render(request,
return render_to_response('context_processors/auth_attrs_test_access.html', 'context_processors/auth_attrs_test_access.html',
{'session_accessed': request.session.accessed}) {'session_accessed': request.session.accessed})
def auth_processor_user(request): def auth_processor_user(request):
return render_to_response('context_processors/auth_attrs_user.html', return render(request, 'context_processors/auth_attrs_user.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth]))
def auth_processor_perms(request): def auth_processor_perms(request):
return render_to_response('context_processors/auth_attrs_perms.html', return render(request, 'context_processors/auth_attrs_perms.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth]))
def auth_processor_perm_in_perms(request): def auth_processor_perm_in_perms(request):
return render_to_response('context_processors/auth_attrs_perm_in_perms.html', return render(request, 'context_processors/auth_attrs_perm_in_perms.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth]))
def auth_processor_messages(request): def auth_processor_messages(request):
info(request, "Message 1") info(request, "Message 1")
return render_to_response('context_processors/auth_attrs_messages.html', return render(request, 'context_processors/auth_attrs_messages.html')
context_instance=RequestContext(request, {}, processors=[context_processors.auth]))
def userpage(request): def userpage(request):

View File

@ -1,7 +1,7 @@
import logging import logging
from django.forms.widgets import Textarea from django.forms.widgets import Textarea
from django.template import loader, Context from django.template import loader
from django.utils import six from django.utils import six
from django.utils import translation from django.utils import translation
@ -10,7 +10,7 @@ from django.contrib.gis.geos import GEOSGeometry, GEOSException
# Creating a template context that contains Django settings # Creating a template context that contains Django settings
# values needed by admin map templates. # values needed by admin map templates.
geo_context = Context({'LANGUAGE_BIDI': translation.get_language_bidi()}) geo_context = {'LANGUAGE_BIDI': translation.get_language_bidi()}
logger = logging.getLogger('django.contrib.gis') logger = logging.getLogger('django.contrib.gis')
@ -81,8 +81,8 @@ class OpenLayersWidget(Textarea):
# geometry. # geometry.
self.params['wkt'] = wkt self.params['wkt'] = wkt
return loader.render_to_string(self.template, self.params, self.params.update(geo_context)
context_instance=geo_context) return loader.render_to_string(self.template, self.params)
def map_options(self): def map_options(self):
"Builds the map options hash for the OpenLayers template." "Builds the map options hash for the OpenLayers template."

View File

@ -3,8 +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.template import loader, RequestContext from django.template import loader, RequestContext
from django.template.context import _current_app_undefined from django.template.context import _current_app_undefined
from django.template.engine import ( from django.template.engine import (
@ -16,44 +14,57 @@ 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(template_name, dictionary=_dictionary_undefined, def render_to_response(template_name, context=None,
context_instance=_context_instance_undefined, context_instance=_context_instance_undefined,
content_type=None, dirs=_dirs_undefined): content_type=None, status=None, dirs=_dirs_undefined,
dictionary=_dictionary_undefined):
""" """
Returns a HttpResponse whose content is filled with the result of calling Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments. django.template.loader.render_to_string() with the passed arguments.
""" """
# TODO: refactor to avoid the deprecated code path. if (context_instance is _context_instance_undefined
with warnings.catch_warnings(): and dirs is _dirs_undefined
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning) and dictionary is _dictionary_undefined):
content = loader.render_to_string(template_name, dictionary, context_instance, dirs) # No deprecated arguments were passed - use the new code path
content = loader.get_template(template_name).render(context)
return HttpResponse(content, content_type) else:
# Some deprecated arguments were passed - use the legacy code path
content = loader.render_to_string(
template_name, context, context_instance, dirs, dictionary)
return HttpResponse(content, content_type, status)
def render(request, template_name, dictionary=_dictionary_undefined, 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, current_app=_current_app_undefined,
dirs=_dirs_undefined): dirs=_dirs_undefined, dictionary=_dictionary_undefined):
""" """
Returns a HttpResponse whose content is filled with the result of calling Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments. django.template.loader.render_to_string() with the passed arguments.
Uses a RequestContext by default. Uses a RequestContext by default.
""" """
if context_instance is not _context_instance_undefined: if (context_instance is _context_instance_undefined
if current_app is not _current_app_undefined: and current_app is _current_app_undefined
raise ValueError('If you provide a context_instance you must ' and dirs is _dirs_undefined
'set its current_app before calling render()') and dictionary is _dictionary_undefined):
else: # No deprecated arguments were passed - use the new code path
context_instance = RequestContext(request, current_app=current_app) content = loader.get_template(template_name).render(context, request)
# TODO: refactor to avoid the deprecated code path. else:
with warnings.catch_warnings(): # Some deprecated arguments were passed - use the legacy code path
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning) if context_instance is not _context_instance_undefined:
content = loader.render_to_string(template_name, dictionary, context_instance, dirs) if current_app is not _current_app_undefined:
raise ValueError('If you provide a context_instance you must '
'set its current_app before calling render()')
else:
context_instance = RequestContext(request, current_app=current_app)
content = loader.render_to_string(
template_name, context, context_instance, dirs, dictionary)
return HttpResponse(content, content_type, status) return HttpResponse(content, content_type, status)

View File

@ -93,6 +93,8 @@ details on these changes.
* The ``dictionary`` and ``context_instance`` parameters for the following * The ``dictionary`` and ``context_instance`` parameters for the following
functions will be removed: functions will be removed:
* ``django.shortcuts.render()``
* ``django.shortcuts.render_to_response()``
* ``django.template.loader.render_to_string()`` * ``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:

View File

@ -95,6 +95,8 @@ you can document in your view function docstrings include:
For example:: For example::
from django.shortcuts import render
from myapp.models import MyModel from myapp.models import MyModel
def my_view(request, slug): def my_view(request, slug):
@ -103,8 +105,6 @@ For example::
**Context** **Context**
``RequestContext``
``mymodel`` ``mymodel``
An instance of :model:`myapp.MyModel`. An instance of :model:`myapp.MyModel`.
@ -113,10 +113,8 @@ For example::
:template:`myapp/my_template.html` :template:`myapp/my_template.html`
""" """
return render_to_response('myapp/my_template.html', { context = {'mymodel': MyModel.objects.get(slug=slug)}
'mymodel': MyModel.objects.get(slug=slug) return render(request, 'myapp/my_template.html', context)
}, context_instance=RequestContext(request))
Template tags and filters reference Template tags and filters reference
=================================== ===================================

View File

@ -1210,6 +1210,8 @@ now optional.
The following functions will no longer accept the ``dictionary`` and The following functions will no longer accept the ``dictionary`` and
``context_instance`` parameters in Django 2.0: ``context_instance`` parameters in Django 2.0:
* ``django.shortcuts.render()``
* ``django.shortcuts.render_to_response()``
* ``django.template.loader.render_to_string()`` * ``django.template.loader.render_to_string()``
Use the ``context`` parameter instead. When ``dictionary`` is passed as a Use the ``context`` parameter instead. When ``dictionary`` is passed as a

View File

@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake.
``render`` ``render``
========== ==========
.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app][, dirs]) .. function:: render(request, template_name[, context][, context_instance][, content_type][, status][, current_app][, dirs])
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.
@ -41,15 +41,24 @@ Required arguments
Optional arguments Optional arguments
------------------ ------------------
``dictionary`` ``context``
A dictionary of values to add to the template context. By default, this A dictionary of values to add to the template context. By default, this
is an empty dictionary. If a value in the dictionary is callable, the is an empty dictionary. If a value in the dictionary is callable, the
view will call it just before rendering the template. view will call it just before rendering the template.
.. 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`` ``context_instance``
The context instance to render the template with. By default, the template The context instance to render the template with. By default, the template
will be rendered with a ``RequestContext`` instance (filled with values from will be rendered with a ``RequestContext`` instance (filled with values from
``request`` and ``dictionary``). ``request`` and ``context``).
.. deprecated:: 1.8
The ``context_instance`` argument is deprecated. Simply use ``context``.
``content_type`` ``content_type``
The MIME type to use for the resulting document. Defaults to the value of The MIME type to use for the resulting document. Defaults to the value of
@ -67,7 +76,7 @@ Optional arguments
The ``dirs`` parameter was added. The ``dirs`` parameter was added.
.. versionchanged:: 1.8 .. deprecated:: 1.8
The ``dirs`` parameter was deprecated. The ``dirs`` parameter was deprecated.
@ -99,7 +108,7 @@ This example is equivalent to::
``render_to_response`` ``render_to_response``
====================== ======================
.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type][, dirs]) .. function:: render_to_response(template_name[, context][, context_instance][, content_type][, status][, dirs])
Renders a given template with a given context dictionary and returns an Renders 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.
@ -116,32 +125,48 @@ Required arguments
Optional arguments Optional arguments
------------------ ------------------
``dictionary`` ``context``
A dictionary of values to add to the template context. By default, this A dictionary of values to add to the template context. By default, this
is an empty dictionary. If a value in the dictionary is callable, the is an empty dictionary. If a value in the dictionary is callable, the
view will call it just before rendering the template. view will call it just before rendering the template.
.. 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`` ``context_instance``
The context instance to render the template with. By default, the template The context instance to render the template with. By default, the template
will be rendered with a :class:`~django.template.Context` instance (filled will be rendered with a :class:`~django.template.Context` instance (filled
with values from ``dictionary``). If you need to use :ref:`context with values from ``context``). If you need to use :ref:`context
processors <subclassing-context-requestcontext>`, render the template with processors <subclassing-context-requestcontext>`, render the template with
a :class:`~django.template.RequestContext` instance instead. Your code a :class:`~django.template.RequestContext` instance instead. Your code
might look something like this:: might look something like this::
return render_to_response('my_template.html', return render_to_response('my_template.html',
my_data_dictionary, my_context,
context_instance=RequestContext(request)) context_instance=RequestContext(request))
.. deprecated:: 1.8
The ``context_instance`` argument is deprecated. Simply use ``context``.
``content_type`` ``content_type``
The MIME type to use for the resulting document. Defaults to the value of The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting. the :setting:`DEFAULT_CONTENT_TYPE` setting.
``status``
The status code for the response. Defaults to ``200``.
.. versionchanged:: 1.8
The ``status`` parameter was added.
.. versionchanged:: 1.7 .. versionchanged:: 1.7
The ``dirs`` parameter was added. The ``dirs`` parameter was added.
.. versionchanged:: 1.8 .. deprecated:: 1.8
The ``dirs`` parameter was deprecated. The ``dirs`` parameter was deprecated.

View File

@ -4,7 +4,10 @@ Tests for Django's bundled context processors.
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
@override_settings(ROOT_URLCONF='context_processors.urls') @override_settings(
ROOT_URLCONF='context_processors.urls',
TEMPLATE_CONTEXT_PROCESSORS=('django.template.context_processors.request',),
)
class RequestContextProcessorTests(TestCase): class RequestContextProcessorTests(TestCase):
""" """
Tests for the ``django.template.context_processors.request`` processor. Tests for the ``django.template.context_processors.request`` processor.
@ -35,7 +38,12 @@ class RequestContextProcessorTests(TestCase):
self.assertContains(response, url) self.assertContains(response, url)
@override_settings(ROOT_URLCONF='context_processors.urls', DEBUG=True, INTERNAL_IPS=('127.0.0.1',)) @override_settings(
DEBUG=True,
INTERNAL_IPS=('127.0.0.1',),
ROOT_URLCONF='context_processors.urls',
TEMPLATE_CONTEXT_PROCESSORS=('django.template.context_processors.debug',),
)
class DebugContextProcessorTests(TestCase): class DebugContextProcessorTests(TestCase):
""" """
Tests for the ``django.template.context_processors.debug`` processor. Tests for the ``django.template.context_processors.debug`` processor.

View File

@ -1,20 +1,12 @@
from django.shortcuts import render_to_response from django.shortcuts import render
from django.template import context_processors
from django.template.context import RequestContext
from .models import DebugObject from .models import DebugObject
def request_processor(request): def request_processor(request):
return render_to_response( return render(request, 'context_processors/request_attrs.html')
'context_processors/request_attrs.html',
context_instance=RequestContext(request, {}, processors=[context_processors.request]))
def debug_processor(request): def debug_processor(request):
context = {'debug_objects': DebugObject.objects}
return render_to_response( return render(request, 'context_processors/debug.html', context)
'context_processors/debug.html',
context_instance=RequestContext(request, {
'debug_objects': DebugObject.objects,
}, processors=[context_processors.debug]))

View File

@ -17,7 +17,9 @@ class ShortcutTests(TestCase):
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
def test_render_to_response_with_request_context(self): def test_render_to_response_with_request_context(self):
response = self.client.get('/render_to_response/request_context/') with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
response = self.client.get('/render_to_response/request_context/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n')
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
@ -42,7 +44,9 @@ class ShortcutTests(TestCase):
RequestContext instance in the dictionary argument instead of the RequestContext instance in the dictionary argument instead of the
context_instance argument. context_instance argument.
""" """
response = self.client.get('/render_to_response/context_instance_misuse/') with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
response = self.client.get('/render_to_response/context_instance_misuse/')
self.assertContains(response, 'context processor output') self.assertContains(response, 'context processor output')
def test_render(self): def test_render(self):
@ -53,7 +57,9 @@ class ShortcutTests(TestCase):
self.assertEqual(response.context.current_app, None) self.assertEqual(response.context.current_app, None)
def test_render_with_base_context(self): def test_render_with_base_context(self):
response = self.client.get('/render/base_context/') with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
response = self.client.get('/render/base_context/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response.content, b'FOO.BAR..\n')
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
@ -70,7 +76,9 @@ class ShortcutTests(TestCase):
self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n')
def test_render_with_current_app(self): def test_render_with_current_app(self):
response = self.client.get('/render/current_app/') with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
response = self.client.get('/render/current_app/')
self.assertEqual(response.context.current_app, "foobar_app") self.assertEqual(response.context.current_app, "foobar_app")
def test_render_with_dirs(self): def test_render_with_dirs(self):
@ -83,4 +91,6 @@ class ShortcutTests(TestCase):
def test_render_with_current_app_conflict(self): def test_render_with_current_app_conflict(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
self.client.get('/render/current_app_conflict/') with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
self.client.get('/render/current_app_conflict/')

View File

@ -6,6 +6,7 @@ from __future__ import unicode_literals
import os import os
import itertools import itertools
import warnings
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.template import TemplateSyntaxError, Context, Template from django.template import TemplateSyntaxError, Context, Template
@ -14,6 +15,7 @@ from django.test.client import RedirectCycleError, RequestFactory, encode_file
from django.test.utils import ContextList, str_prefix from django.test.utils import ContextList, str_prefix
from django.template.response import SimpleTemplateResponse from django.template.response import SimpleTemplateResponse
from django.utils._os import upath from django.utils._os import upath
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from django.http import HttpResponse from django.http import HttpResponse
from django.contrib.auth.signals import user_logged_out, user_logged_in from django.contrib.auth.signals import user_logged_out, user_logged_in
@ -999,14 +1001,16 @@ class ContextTests(TestCase):
l.keys()) l.keys())
def test_15368(self): def test_15368(self):
# Need to insert a context processor that assumes certain things about with warnings.catch_warnings():
# the request instance. This triggers a bug caused by some ways of warnings.filterwarnings("ignore", category=RemovedInDjango20Warning)
# copying RequestContext. # Need to insert a context processor that assumes certain things about
with self.settings(TEMPLATE_CONTEXT_PROCESSORS=( # the request instance. This triggers a bug caused by some ways of
'test_client_regress.context_processors.special', # copying RequestContext.
)): with self.settings(TEMPLATE_CONTEXT_PROCESSORS=(
response = self.client.get("/request_context_view/") 'test_client_regress.context_processors.special',
self.assertContains(response, 'Path: /request_context_view/') )):
response = self.client.get("/request_context_view/")
self.assertContains(response, 'Path: /request_context_view/')
def test_nested_requests(self): def test_nested_requests(self):
""" """