Changed RedirectView.permanent to False per deprecation timeline; refs #21587.

This commit is contained in:
Tim Graham 2015-01-18 16:43:57 -05:00
parent bd98926f0e
commit 6e13c0490d
8 changed files with 69 additions and 159 deletions

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals
import logging
import warnings
from functools import update_wrapper
from django import http
@ -9,10 +8,8 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse
from django.utils.decorators import classonlymethod
from django.utils.deprecation import RemovedInDjango19Warning
from django.utils import six
_sentinel = object()
logger = logging.getLogger('django.request')
@ -163,36 +160,11 @@ class RedirectView(View):
"""
A view that provides a redirect on any GET request.
"""
permanent = _sentinel
permanent = False
url = None
pattern_name = None
query_string = False
def __init__(self, *args, **kwargs):
if 'permanent' not in kwargs and self.permanent is _sentinel:
warnings.warn(
"Default value of 'RedirectView.permanent' will change "
"from True to False in Django 1.9. Set an explicit value "
"to silence this warning.",
RemovedInDjango19Warning,
stacklevel=2
)
self.permanent = True
super(RedirectView, self).__init__(*args, **kwargs)
@classonlymethod
def as_view(cls, **initkwargs):
if 'permanent' not in initkwargs and cls.permanent is _sentinel:
warnings.warn(
"Default value of 'RedirectView.permanent' will change "
"from True to False in Django 1.9. Set an explicit value "
"to silence this warning.",
RemovedInDjango19Warning,
stacklevel=2
)
initkwargs['permanent'] = True
return super(RedirectView, cls).as_view(**initkwargs)
def get_redirect_url(self, *args, **kwargs):
"""
Return the URL redirect to. Keyword arguments from the

View File

@ -225,12 +225,12 @@ RedirectView
Whether the redirect should be permanent. The only difference here is
the HTTP status code returned. If ``True``, then the redirect will use
status code 301. If ``False``, then the redirect will use status code
302. By default, ``permanent`` is ``True``.
302. By default, ``permanent`` is ``False``.
.. deprecated:: 1.8
.. versionchanged:: 1.9
The default value of the ``permanent`` attribute will change from
``True`` to ``False`` in Django 1.9.
The default value of the ``permanent`` attribute changed from
``True`` to ``False``.
.. attribute:: query_string

View File

@ -2,14 +2,11 @@ from __future__ import unicode_literals
import time
import unittest
import warnings
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import resolve
from django.http import HttpResponse
from django.utils import six
from django.utils.deprecation import RemovedInDjango19Warning
from django.test import TestCase, RequestFactory, ignore_warnings, override_settings
from django.test import TestCase, RequestFactory, override_settings
from django.views.generic import View, TemplateView, RedirectView
from . import views
@ -340,7 +337,6 @@ class TemplateViewTest(TestCase):
self.assertIs(match.func.view_class, TemplateView)
@ignore_warnings(category=RemovedInDjango19Warning)
@override_settings(ROOT_URLCONF='generic_views.urls')
class RedirectViewTest(TestCase):
@ -351,14 +347,20 @@ class RedirectViewTest(TestCase):
response = RedirectView.as_view()(self.rf.get('/foo/'))
self.assertEqual(response.status_code, 410)
def test_permanent_redirect(self):
"Default is a permanent redirect"
def test_default_redirect(self):
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/'))
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_permanent_redirect(self):
"Permanent redirects are an option"
response = RedirectView.as_view(url='/bar/', permanent=True)(self.rf.get('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.url, '/bar/')
def test_temporary_redirect(self):
"Permanent redirects are an option"
"Temporary redirects are an option"
response = RedirectView.as_view(url='/bar/', permanent=False)(self.rf.get('/foo/'))
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
@ -366,35 +368,35 @@ class RedirectViewTest(TestCase):
def test_include_args(self):
"GET arguments can be included in the redirected URL"
response = RedirectView.as_view(url='/bar/')(self.rf.get('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
response = RedirectView.as_view(url='/bar/', query_string=True)(self.rf.get('/foo/?pork=spam'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/?pork=spam')
def test_include_urlencoded_args(self):
"GET arguments can be URL-encoded when included in the redirected URL"
response = RedirectView.as_view(url='/bar/', query_string=True)(
self.rf.get('/foo/?unicode=%E2%9C%93'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/?unicode=%E2%9C%93')
def test_parameter_substitution(self):
"Redirection URLs can be parameterized"
response = RedirectView.as_view(url='/bar/%(object_id)d/')(self.rf.get('/foo/42/'), object_id=42)
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/42/')
def test_named_url_pattern(self):
"Named pattern parameter should reverse to the matching pattern"
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1)
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/detail/artist/1/')
def test_named_url_pattern_using_args(self):
response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1)
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/detail/artist/1/')
def test_wrong_named_url_pattern(self):
@ -403,46 +405,46 @@ class RedirectViewTest(TestCase):
self.assertEqual(response.status_code, 410)
def test_redirect_POST(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_HEAD(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.head('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_OPTIONS(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.options('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_PUT(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.put('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_PATCH(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.patch('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_DELETE(self):
"Default is a permanent redirect"
"Default is a temporary redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.delete('/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, '/bar/')
def test_redirect_when_meta_contains_no_query_string(self):
"regression for #16705"
# we can't use self.rf.get because it always sets QUERY_STRING
response = RedirectView.as_view(url='/bar/')(self.rf.request(PATH_INFO='/foo/'))
self.assertEqual(response.status_code, 301)
self.assertEqual(response.status_code, 302)
def test_direct_instantiation(self):
"""
@ -454,69 +456,6 @@ class RedirectViewTest(TestCase):
self.assertEqual(response.status_code, 410)
@override_settings(ROOT_URLCONF='generic_views.urls')
class RedirectViewDeprecationTest(TestCase):
rf = RequestFactory()
def test_deprecation_warning_init(self):
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
view = RedirectView()
response = view.dispatch(self.rf.head('/python/'))
self.assertEqual(response.status_code, 410)
self.assertEqual(len(warns), 1)
self.assertIs(warns[0].category, RemovedInDjango19Warning)
self.assertEqual(
six.text_type(warns[0].message),
"Default value of 'RedirectView.permanent' will change "
"from True to False in Django 1.9. Set an explicit value "
"to silence this warning.",
)
def test_deprecation_warning_raised_when_permanent_not_passed(self):
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
view_function = RedirectView.as_view(url='/bbb/')
request = self.rf.request(PATH_INFO='/aaa/')
view_function(request)
self.assertEqual(len(warns), 1)
self.assertIs(warns[0].category, RemovedInDjango19Warning)
self.assertEqual(
six.text_type(warns[0].message),
"Default value of 'RedirectView.permanent' will change "
"from True to False in Django 1.9. Set an explicit value "
"to silence this warning.",
)
def test_no_deprecation_warning_when_permanent_passed(self):
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
view_function = RedirectView.as_view(url='/bar/', permanent=False)
request = self.rf.request(PATH_INFO='/foo/')
view_function(request)
self.assertEqual(len(warns), 0)
def test_no_deprecation_warning_with_custom_redirectview(self):
class CustomRedirectView(RedirectView):
permanent = False
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
view_function = CustomRedirectView.as_view(url='/eggs/')
request = self.rf.request(PATH_INFO='/spam/')
view_function(request)
self.assertEqual(len(warns), 0)
class GetContextDataTest(unittest.TestCase):
def test_get_context_data_super(self):

View File

@ -15,8 +15,8 @@ urlpatterns = [
url(r'^secure_view/$', views.view_with_secure),
url(r'^permanent_redirect_view/$', RedirectView.as_view(url='/get_view/', permanent=True)),
url(r'^temporary_redirect_view/$', RedirectView.as_view(url='/get_view/', permanent=False)),
url(r'^http_redirect_view/$', RedirectView.as_view(url='/secure_view/', permanent=True)),
url(r'^https_redirect_view/$', RedirectView.as_view(url='https://testserver/secure_view/', permanent=True)),
url(r'^http_redirect_view/$', RedirectView.as_view(url='/secure_view/')),
url(r'^https_redirect_view/$', RedirectView.as_view(url='https://testserver/secure_view/')),
url(r'^double_redirect_view/$', views.double_redirect_view),
url(r'^bad_view/$', views.bad_view),
url(r'^form_view/$', views.form_view),

View File

@ -353,27 +353,27 @@ class AssertRedirectsTests(TestCase):
"You can follow a redirect chain of multiple redirects"
response = self.client.get('/redirects/further/more/', {}, follow=True)
self.assertRedirects(response, '/no_template_view/',
status_code=301, target_status_code=200)
status_code=302, target_status_code=200)
self.assertEqual(len(response.redirect_chain), 1)
self.assertEqual(response.redirect_chain[0], ('http://testserver/no_template_view/', 301))
self.assertEqual(response.redirect_chain[0], ('http://testserver/no_template_view/', 302))
def test_multiple_redirect_chain(self):
"You can follow a redirect chain of multiple redirects"
response = self.client.get('/redirects/', {}, follow=True)
self.assertRedirects(response, '/no_template_view/',
status_code=301, target_status_code=200)
status_code=302, target_status_code=200)
self.assertEqual(len(response.redirect_chain), 3)
self.assertEqual(response.redirect_chain[0], ('http://testserver/redirects/further/', 301))
self.assertEqual(response.redirect_chain[1], ('http://testserver/redirects/further/more/', 301))
self.assertEqual(response.redirect_chain[2], ('http://testserver/no_template_view/', 301))
self.assertEqual(response.redirect_chain[0], ('http://testserver/redirects/further/', 302))
self.assertEqual(response.redirect_chain[1], ('http://testserver/redirects/further/more/', 302))
self.assertEqual(response.redirect_chain[2], ('http://testserver/no_template_view/', 302))
def test_redirect_chain_to_non_existent(self):
"You can follow a chain to a non-existent view"
response = self.client.get('/redirect_to_non_existent_view2/', {}, follow=True)
self.assertRedirects(response, '/non_existent_view/',
status_code=301, target_status_code=404)
status_code=302, target_status_code=404)
def test_redirect_chain_to_self(self):
"Redirections to self are caught and escaped"
@ -382,7 +382,7 @@ class AssertRedirectsTests(TestCase):
response = context.exception.last_response
# The chain of redirects stops once the cycle is detected.
self.assertRedirects(response, '/redirect_to_self/',
status_code=301, target_status_code=301)
status_code=302, target_status_code=302)
self.assertEqual(len(response.redirect_chain), 2)
def test_redirect_to_self_with_changing_query(self):
@ -397,7 +397,7 @@ class AssertRedirectsTests(TestCase):
response = context.exception.last_response
# The chain of redirects will get back to the starting point, but stop there.
self.assertRedirects(response, '/circular_redirect_2/',
status_code=301, target_status_code=301)
status_code=302, target_status_code=302)
self.assertEqual(len(response.redirect_chain), 4)
def test_redirect_chain_post(self):
@ -405,7 +405,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.post('/redirects/',
{'nothing': 'to_send'}, follow=True)
self.assertRedirects(response,
'/no_template_view/', 301, 200)
'/no_template_view/', 302, 200)
self.assertEqual(len(response.redirect_chain), 3)
def test_redirect_chain_head(self):
@ -413,7 +413,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.head('/redirects/',
{'nothing': 'to_send'}, follow=True)
self.assertRedirects(response,
'/no_template_view/', 301, 200)
'/no_template_view/', 302, 200)
self.assertEqual(len(response.redirect_chain), 3)
def test_redirect_chain_options(self):
@ -421,7 +421,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.options('/redirects/',
follow=True)
self.assertRedirects(response,
'/no_template_view/', 301, 200)
'/no_template_view/', 302, 200)
self.assertEqual(len(response.redirect_chain), 3)
def test_redirect_chain_put(self):
@ -429,7 +429,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.put('/redirects/',
follow=True)
self.assertRedirects(response,
'/no_template_view/', 301, 200)
'/no_template_view/', 302, 200)
self.assertEqual(len(response.redirect_chain), 3)
def test_redirect_chain_delete(self):
@ -437,7 +437,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.delete('/redirects/',
follow=True)
self.assertRedirects(response,
'/no_template_view/', 301, 200)
'/no_template_view/', 302, 200)
self.assertEqual(len(response.redirect_chain), 3)
def test_redirect_to_different_host(self):
@ -445,7 +445,7 @@ class AssertRedirectsTests(TestCase):
response = self.client.get('/redirect_other_host/', follow=True)
self.assertRedirects(response,
'https://otherserver:8443/no_template_view/',
status_code=301, target_status_code=200)
status_code=302, target_status_code=200)
# We can't use is_secure() or get_host()
# because response.request is a dictionary, not an HttpRequest
self.assertEqual(response.request.get('wsgi.url_scheme'), 'https')
@ -492,11 +492,11 @@ class AssertRedirectsTests(TestCase):
# always redirects to https
response = self.client.get('/https_redirect_view/', follow=follow, secure=secure)
# no scheme to compare too, always succeeds
self.assertRedirects(response, '/secure_view/', status_code=301)
self.assertRedirects(response, '/secure_view/', status_code=302)
# the goal scheme is https
self.assertRedirects(response, 'https://testserver/secure_view/', status_code=301)
self.assertRedirects(response, 'https://testserver/secure_view/', status_code=302)
with self.assertRaises(AssertionError):
self.assertRedirects(response, 'http://testserver/secure_view/', status_code=301)
self.assertRedirects(response, 'http://testserver/secure_view/', status_code=302)
@override_settings(ROOT_URLCONF='test_client_regress.urls')
@ -1369,7 +1369,7 @@ class RequestHeadersTest(TestCase):
response = self.client.get("/check_headers_redirect/", follow=True, HTTP_X_ARG_CHECK='Testing 123')
self.assertEqual(response.content, b"HTTP_X_ARG_CHECK: Testing 123")
self.assertRedirects(response, '/check_headers/',
status_code=301, target_status_code=200)
status_code=302, target_status_code=200)
@override_settings(ROOT_URLCONF='test_client_regress.urls')

View File

@ -3,7 +3,7 @@ from django.views.generic import RedirectView
from . import views
# TODO: Remove permanent=True where it doesn't matter in Django 1.9
urlpatterns = [
url(r'', include('test_client.urls')),
@ -15,17 +15,17 @@ urlpatterns = [
url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
url(r'^nested_view/$', views.nested_view, name='nested_view'),
url(r'^login_protected_redirect_view/$', views.login_protected_redirect_view),
url(r'^redirects/$', RedirectView.as_view(url='/redirects/further/', permanent=True)),
url(r'^redirects/further/$', RedirectView.as_view(url='/redirects/further/more/', permanent=True)),
url(r'^redirects/further/more/$', RedirectView.as_view(url='/no_template_view/', permanent=True)),
url(r'^redirect_to_non_existent_view/$', RedirectView.as_view(url='/non_existent_view/', permanent=True)),
url(r'^redirect_to_non_existent_view2/$', RedirectView.as_view(url='/redirect_to_non_existent_view/', permanent=True)),
url(r'^redirect_to_self/$', RedirectView.as_view(url='/redirect_to_self/', permanent=True)),
url(r'^redirects/$', RedirectView.as_view(url='/redirects/further/')),
url(r'^redirects/further/$', RedirectView.as_view(url='/redirects/further/more/')),
url(r'^redirects/further/more/$', RedirectView.as_view(url='/no_template_view/')),
url(r'^redirect_to_non_existent_view/$', RedirectView.as_view(url='/non_existent_view/')),
url(r'^redirect_to_non_existent_view2/$', RedirectView.as_view(url='/redirect_to_non_existent_view/')),
url(r'^redirect_to_self/$', RedirectView.as_view(url='/redirect_to_self/')),
url(r'^redirect_to_self_with_changing_query_view/$', views.redirect_to_self_with_changing_query_view),
url(r'^circular_redirect_1/$', RedirectView.as_view(url='/circular_redirect_2/', permanent=True)),
url(r'^circular_redirect_2/$', RedirectView.as_view(url='/circular_redirect_3/', permanent=True)),
url(r'^circular_redirect_3/$', RedirectView.as_view(url='/circular_redirect_1/', permanent=True)),
url(r'^redirect_other_host/$', RedirectView.as_view(url='https://otherserver:8443/no_template_view/', permanent=True)),
url(r'^circular_redirect_1/$', RedirectView.as_view(url='/circular_redirect_2/')),
url(r'^circular_redirect_2/$', RedirectView.as_view(url='/circular_redirect_3/')),
url(r'^circular_redirect_3/$', RedirectView.as_view(url='/circular_redirect_1/')),
url(r'^redirect_other_host/$', RedirectView.as_view(url='https://otherserver:8443/no_template_view/')),
url(r'^set_session/$', views.set_session_view),
url(r'^check_session/$', views.check_session_view),
url(r'^request_methods/$', views.request_methods_view),
@ -33,7 +33,7 @@ urlpatterns = [
url(r'^check_binary/$', views.return_undecodable_binary),
url(r'^parse_unicode_json/$', views.return_json_file),
url(r'^check_headers/$', views.check_headers),
url(r'^check_headers_redirect/$', RedirectView.as_view(url='/check_headers/', permanent=True)),
url(r'^check_headers_redirect/$', RedirectView.as_view(url='/check_headers/')),
url(r'^body/$', views.body),
url(r'^read_all/$', views.read_all),
url(r'^read_buffer/$', views.read_buffer),

View File

@ -6,6 +6,5 @@ urlpatterns = [
url(r'^redirected_to/$', empty_view, name='named-lazy-url-redirected-to'),
url(r'^login/$', empty_view, name='some-login-page'),
url(r'^login_required_view/$', login_required_view),
# TODO: Remove permanent=True where it doesn't matter in Django 1.9
url(r'^redirect/$', LazyRedirectView.as_view(permanent=True)),
url(r'^redirect/$', LazyRedirectView.as_view()),
]

View File

@ -308,7 +308,7 @@ class ReverseLazyTest(TestCase):
def test_redirect_with_lazy_reverse(self):
response = self.client.get('/redirect/')
self.assertRedirects(response, "/redirected_to/", status_code=301)
self.assertRedirects(response, "/redirected_to/", status_code=302)
def test_user_permission_with_lazy_reverse(self):
User.objects.create_user('alfred', 'alfred@example.com', password='testpw')