mirror of https://github.com/django/django.git
Fixed #15273 -- Extend RedirectView to allow reversal by name.
Thanks to @DrMeers for the report and @ludwigkraatz for the initial patch.
This commit is contained in:
parent
8365ed08b8
commit
b7bd7087e6
|
@ -5,6 +5,7 @@ from functools import update_wrapper
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils.decorators import classonlymethod
|
from django.utils.decorators import classonlymethod
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -160,9 +161,10 @@ class RedirectView(View):
|
||||||
"""
|
"""
|
||||||
permanent = True
|
permanent = True
|
||||||
url = None
|
url = None
|
||||||
|
pattern_name = None
|
||||||
query_string = False
|
query_string = False
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the URL redirect to. Keyword arguments from the
|
Return the URL redirect to. Keyword arguments from the
|
||||||
URL pattern match generating the redirect request
|
URL pattern match generating the redirect request
|
||||||
|
@ -170,15 +172,21 @@ class RedirectView(View):
|
||||||
"""
|
"""
|
||||||
if self.url:
|
if self.url:
|
||||||
url = self.url % kwargs
|
url = self.url % kwargs
|
||||||
|
elif self.pattern_name:
|
||||||
|
try:
|
||||||
|
url = reverse(self.pattern_name, args=args, kwargs=kwargs)
|
||||||
|
except NoReverseMatch:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
args = self.request.META.get('QUERY_STRING', '')
|
args = self.request.META.get('QUERY_STRING', '')
|
||||||
if args and self.query_string:
|
if args and self.query_string:
|
||||||
url = "%s?%s" % (url, args)
|
url = "%s?%s" % (url, args)
|
||||||
return url
|
return url
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
url = self.get_redirect_url(**kwargs)
|
url = self.get_redirect_url(*args, **kwargs)
|
||||||
if url:
|
if url:
|
||||||
if self.permanent:
|
if self.permanent:
|
||||||
return http.HttpResponsePermanentRedirect(url)
|
return http.HttpResponsePermanentRedirect(url)
|
||||||
|
|
|
@ -192,22 +192,24 @@ RedirectView
|
||||||
|
|
||||||
permanent = False
|
permanent = False
|
||||||
query_string = True
|
query_string = True
|
||||||
|
pattern_name = 'article-detail'
|
||||||
|
|
||||||
def get_redirect_url(self, pk):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
article = get_object_or_404(Article, pk=pk)
|
article = get_object_or_404(Article, pk=pk)
|
||||||
article.update_counter()
|
article.update_counter()
|
||||||
return reverse('product_detail', args=(pk,))
|
return super(ArticleCounterRedirectView, self).get_redirect_url(*args, **kwargs)
|
||||||
|
|
||||||
**Example urls.py**::
|
**Example urls.py**::
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
|
|
||||||
from article.views import ArticleCounterRedirectView
|
from article.views import ArticleCounterRedirectView, ArticleDetail
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
|
url(r'^counter/(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
|
||||||
|
url(r'^details/(?P<pk>\d+)/$', ArticleDetail.as_view(), name='article-detail'),
|
||||||
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
|
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -218,6 +220,11 @@ RedirectView
|
||||||
The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
|
The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
|
||||||
HTTP error.
|
HTTP error.
|
||||||
|
|
||||||
|
.. attribute:: pattern_name
|
||||||
|
|
||||||
|
The name of the URL pattern to redirect to. Reversing will be done
|
||||||
|
using the same args and kwargs as are passed in for this view.
|
||||||
|
|
||||||
.. attribute:: permanent
|
.. attribute:: permanent
|
||||||
|
|
||||||
Whether the redirect should be permanent. The only difference here is
|
Whether the redirect should be permanent. The only difference here is
|
||||||
|
|
|
@ -688,6 +688,9 @@ Miscellaneous
|
||||||
|
|
||||||
url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete')
|
url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete')
|
||||||
|
|
||||||
|
* :class:`~django.views.generic.base.RedirectView` now has a `pattern_name`
|
||||||
|
attribute which allows it to choose the target by reversing the URL.
|
||||||
|
|
||||||
Features deprecated in 1.6
|
Features deprecated in 1.6
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -317,7 +317,9 @@ class TemplateViewTest(TestCase):
|
||||||
self.assertEqual(response['Content-Type'], 'text/plain')
|
self.assertEqual(response['Content-Type'], 'text/plain')
|
||||||
|
|
||||||
|
|
||||||
class RedirectViewTest(unittest.TestCase):
|
class RedirectViewTest(TestCase):
|
||||||
|
urls = 'generic_views.urls'
|
||||||
|
|
||||||
rf = RequestFactory()
|
rf = RequestFactory()
|
||||||
|
|
||||||
def test_no_url(self):
|
def test_no_url(self):
|
||||||
|
@ -360,6 +362,22 @@ class RedirectViewTest(unittest.TestCase):
|
||||||
self.assertEqual(response.status_code, 301)
|
self.assertEqual(response.status_code, 301)
|
||||||
self.assertEqual(response.url, '/bar/42/')
|
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['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['Location'], '/detail/artist/1/')
|
||||||
|
|
||||||
|
def test_wrong_named_url_pattern(self):
|
||||||
|
"A wrong pattern name returns 410 GONE"
|
||||||
|
response = RedirectView.as_view(pattern_name='wrong.pattern_name')(self.rf.get('/foo/'))
|
||||||
|
self.assertEqual(response.status_code, 410)
|
||||||
|
|
||||||
def test_redirect_POST(self):
|
def test_redirect_POST(self):
|
||||||
"Default is a permanent redirect"
|
"Default is a permanent redirect"
|
||||||
response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/'))
|
response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/'))
|
||||||
|
|
Loading…
Reference in New Issue