Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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

Thanks to @DrMeers for the report and @ludwigkraatz for the initial patch.
  • Loading branch information...
commit b7bd7087e6480c6a071a12ce20b389a8c225e908 1 parent 8365ed0
@mjtamlyn mjtamlyn authored
View
20 django/views/generic/base.py
@@ -5,6 +5,7 @@
from django import http
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 import six
@@ -160,9 +161,10 @@ class RedirectView(View):
"""
permanent = True
url = None
+ pattern_name = None
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
URL pattern match generating the redirect request
@@ -170,15 +172,21 @@ def get_redirect_url(self, **kwargs):
"""
if self.url:
url = self.url % kwargs
- args = self.request.META.get('QUERY_STRING', '')
- if args and self.query_string:
- url = "%s?%s" % (url, args)
- return url
+ 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', '')
+ if args and self.query_string:
+ url = "%s?%s" % (url, args)
+ return url
+
def get(self, request, *args, **kwargs):
- url = self.get_redirect_url(**kwargs)
+ url = self.get_redirect_url(*args, **kwargs)
if url:
if self.permanent:
return http.HttpResponsePermanentRedirect(url)
View
15 docs/ref/class-based-views/base.txt
@@ -192,22 +192,24 @@ RedirectView
permanent = False
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.update_counter()
- return reverse('product_detail', args=(pk,))
+ return super(ArticleCounterRedirectView, self).get_redirect_url(*args, **kwargs)
**Example urls.py**::
from django.conf.urls import patterns, url
from django.views.generic.base import RedirectView
- from article.views import ArticleCounterRedirectView
+ from article.views import ArticleCounterRedirectView, ArticleDetail
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'),
)
@@ -218,6 +220,11 @@ RedirectView
The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
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
Whether the redirect should be permanent. The only difference here is
View
3  docs/releases/1.6.txt
@@ -688,6 +688,9 @@ Miscellaneous
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
==========================
View
20 tests/generic_views/test_base.py
@@ -317,7 +317,9 @@ def test_content_type(self):
self.assertEqual(response['Content-Type'], 'text/plain')
-class RedirectViewTest(unittest.TestCase):
+class RedirectViewTest(TestCase):
+ urls = 'generic_views.urls'
+
rf = RequestFactory()
def test_no_url(self):
@@ -360,6 +362,22 @@ def test_parameter_substitution(self):
self.assertEqual(response.status_code, 301)
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):
"Default is a permanent redirect"
response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/'))

0 comments on commit b7bd708

Please sign in to comment.
Something went wrong with that request. Please try again.