Fixed #19878 -- Deprecated TemplateView passing URL kwargs into context.

This commit is contained in:
Adam Johnson 2019-08-15 06:48:33 +01:00 committed by Mariusz Felisiak
parent f982f0bdb8
commit 4ed534758c
6 changed files with 84 additions and 29 deletions

View File

@ -1,4 +1,5 @@
import logging
import warnings
from functools import update_wrapper
from django.core.exceptions import ImproperlyConfigured
@ -9,6 +10,8 @@ from django.http import (
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.decorators import classonlymethod
from django.utils.deprecation import RemovedInDjango40Warning
from django.utils.functional import SimpleLazyObject
logger = logging.getLogger('django.request')
@ -152,14 +155,33 @@ class TemplateResponseMixin:
class TemplateView(TemplateResponseMixin, ContextMixin, View):
"""
Render a template. Pass keyword arguments from the URLconf to the context.
"""
"""Render a template."""
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
# RemovedInDjango40Warning: when the deprecation ends, replace with:
# context = self.get_context_data()
context_kwargs = _wrap_url_kwargs_with_deprecation_warning(kwargs)
context = self.get_context_data(**context_kwargs)
return self.render_to_response(context)
# RemovedInDjango40Warning
def _wrap_url_kwargs_with_deprecation_warning(url_kwargs):
context_kwargs = {}
for key, value in url_kwargs.items():
# Bind into function closure.
@SimpleLazyObject
def access_value(key=key, value=value):
warnings.warn(
'TemplateView passing URL kwargs to the context is '
'deprecated. Reference %s in your template through '
'view.kwargs instead.' % key,
RemovedInDjango40Warning, stacklevel=2,
)
return value
context_kwargs[key] = access_value
return context_kwargs
class RedirectView(View):
"""Provide a redirect on any GET request."""
permanent = False

View File

@ -66,6 +66,9 @@ details on these changes.
* The ``list`` message for ``ModelMultipleChoiceField`` will be removed.
* ``django.views.generic.TemplateView`` will no longer pass URL kwargs directly
to the ``context``.
See the :ref:`Django 3.1 release notes <deprecated-features-3.1>` for more
details on these changes.

View File

@ -117,8 +117,7 @@ MRO is an acronym for Method Resolution Order.
.. class:: django.views.generic.base.TemplateView
Renders a given template, with the context containing parameters captured
in the URL.
Renders a given template.
**Ancestors (MRO)**
@ -162,12 +161,17 @@ MRO is an acronym for Method Resolution Order.
**Context**
* Populated (through :class:`~django.views.generic.base.ContextMixin`) with
the keyword arguments captured from the URL pattern that served the view.
* Populated (through :class:`~django.views.generic.base.ContextMixin`).
* You can also add context using the
:attr:`~django.views.generic.base.ContextMixin.extra_context` keyword
argument for :meth:`~django.views.generic.base.View.as_view`.
.. deprecated:: 3.1
Starting in Django 4.0, the keyword arguments captured from the URL
pattern won't be passed to the context. Reference them with
``view.kwargs`` instead.
``RedirectView``
================

View File

@ -640,6 +640,10 @@ Miscellaneous
* The ``list`` message for :class:`~django.forms.ModelMultipleChoiceField` is
deprecated in favor of ``invalid_list``.
* The passing of URL kwargs directly to the context by
:class:`~django.views.generic.base.TemplateView` is deprecated. Reference
them in the template with ``view.kwargs`` instead.
.. _removed-features-3.1:
Features removed in 3.1

View File

@ -2,9 +2,12 @@ import time
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse
from django.test import RequestFactory, SimpleTestCase, override_settings
from django.test import (
RequestFactory, SimpleTestCase, ignore_warnings, override_settings,
)
from django.test.utils import require_jinja2
from django.urls import resolve
from django.utils.deprecation import RemovedInDjango40Warning
from django.views.generic import RedirectView, TemplateView, View
from . import views
@ -347,25 +350,6 @@ class TemplateViewTest(SimpleTestCase):
view = TemplateView.as_view(template_name='generic_views/using.html', template_engine='jinja2')
self.assertEqual(view(request).render().content, b'Jinja2\n')
def test_template_params(self):
"""
A generic template view passes kwargs as context.
"""
response = self.client.get('/template/simple/bar/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['foo'], 'bar')
self.assertIsInstance(response.context['view'], View)
def test_extra_template_params(self):
"""
A template view can be customized to return extra context.
"""
response = self.client.get('/template/custom/bar/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['foo'], 'bar')
self.assertEqual(response.context['key'], 'value')
self.assertIsInstance(response.context['view'], View)
def test_cached_views(self):
"""
A template view can be cached
@ -584,3 +568,38 @@ class SingleObjectTemplateResponseMixinTest(SimpleTestCase):
)
with self.assertRaisesMessage(ImproperlyConfigured, msg):
view.get_template_names()
@override_settings(ROOT_URLCONF='generic_views.urls')
class DeprecationTests(SimpleTestCase):
@ignore_warnings(category=RemovedInDjango40Warning)
def test_template_params(self):
"""A generic template view passes kwargs as context."""
response = self.client.get('/template/simple/bar/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['foo'], 'bar')
self.assertIsInstance(response.context['view'], View)
@ignore_warnings(category=RemovedInDjango40Warning)
def test_extra_template_params(self):
"""A template view can be customized to return extra context."""
response = self.client.get('/template/custom/bar1/bar2/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['foo1'], 'bar1')
self.assertEqual(response.context['foo2'], 'bar2')
self.assertEqual(response.context['key'], 'value')
self.assertIsInstance(response.context['view'], View)
def test_template_params_warning(self):
response = self.client.get('/template/custom/bar1/bar2/')
self.assertEqual(response.status_code, 200)
msg = (
'TemplateView passing URL kwargs to the context is deprecated. '
'Reference %s in your template through view.kwargs instead.'
)
with self.assertRaisesMessage(RemovedInDjango40Warning, msg % 'foo1'):
str(response.context['foo1'])
with self.assertRaisesMessage(RemovedInDjango40Warning, msg % 'foo2'):
str(response.context['foo2'])
self.assertEqual(response.context['key'], 'value')
self.assertIsInstance(response.context['view'], View)

View File

@ -12,7 +12,10 @@ urlpatterns = [
path('template/no_template/', TemplateView.as_view()),
path('template/login_required/', login_required(TemplateView.as_view())),
path('template/simple/<foo>/', TemplateView.as_view(template_name='generic_views/about.html')),
path('template/custom/<foo>/', views.CustomTemplateView.as_view(template_name='generic_views/about.html')),
path(
'template/custom/<foo1>/<foo2>/',
views.CustomTemplateView.as_view(template_name='generic_views/about.html'),
),
path(
'template/content_type/',
TemplateView.as_view(template_name='generic_views/robots.txt', content_type='text/plain'),