Kaydet (Commit) b7bd7087 authored tarafından Marc Tamlyn's avatar Marc Tamlyn

Fixed #15273 -- Extend RedirectView to allow reversal by name.

Thanks to @DrMeers for the report and @ludwigkraatz for the initial patch.
üst 8365ed08
...@@ -5,6 +5,7 @@ from functools import update_wrapper ...@@ -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): ...@@ -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): ...@@ -170,15 +172,21 @@ class RedirectView(View):
""" """
if self.url: if self.url:
url = self.url % kwargs url = self.url % kwargs
args = self.request.META.get('QUERY_STRING', '') elif self.pattern_name:
if args and self.query_string: try:
url = "%s?%s" % (url, args) url = reverse(self.pattern_name, args=args, kwargs=kwargs)
return url except NoReverseMatch:
return None
else: else:
return None return None
args = self.request.META.get('QUERY_STRING', '')
if args and self.query_string:
url = "%s?%s" % (url, args)
return url
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 ...@@ -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 ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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/'))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment