Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Marc Tamlyn authored June 14, 2013
20  django/views/generic/base.py
@@ -5,6 +5,7 @@
5 5
 
6 6
 from django import http
7 7
 from django.core.exceptions import ImproperlyConfigured
  8
+from django.core.urlresolvers import reverse, NoReverseMatch
8 9
 from django.template.response import TemplateResponse
9 10
 from django.utils.decorators import classonlymethod
10 11
 from django.utils import six
@@ -160,9 +161,10 @@ class RedirectView(View):
160 161
     """
161 162
     permanent = True
162 163
     url = None
  164
+    pattern_name = None
163 165
     query_string = False
164 166
 
165  
-    def get_redirect_url(self, **kwargs):
  167
+    def get_redirect_url(self, *args, **kwargs):
166 168
         """
167 169
         Return the URL redirect to. Keyword arguments from the
168 170
         URL pattern match generating the redirect request
@@ -170,15 +172,21 @@ def get_redirect_url(self, **kwargs):
170 172
         """
171 173
         if self.url:
172 174
             url = self.url % kwargs
173  
-            args = self.request.META.get('QUERY_STRING', '')
174  
-            if args and self.query_string:
175  
-                url = "%s?%s" % (url, args)
176  
-            return url
  175
+        elif self.pattern_name:
  176
+            try:
  177
+                url = reverse(self.pattern_name, args=args, kwargs=kwargs)
  178
+            except NoReverseMatch:
  179
+                return None
177 180
         else:
178 181
             return None
179 182
 
  183
+        args = self.request.META.get('QUERY_STRING', '')
  184
+        if args and self.query_string:
  185
+            url = "%s?%s" % (url, args)
  186
+        return url
  187
+
180 188
     def get(self, request, *args, **kwargs):
181  
-        url = self.get_redirect_url(**kwargs)
  189
+        url = self.get_redirect_url(*args, **kwargs)
182 190
         if url:
183 191
             if self.permanent:
184 192
                 return http.HttpResponsePermanentRedirect(url)
15  docs/ref/class-based-views/base.txt
@@ -192,22 +192,24 @@ RedirectView
192 192
 
193 193
             permanent = False
194 194
             query_string = True
  195
+            pattern_name = 'article-detail'
195 196
 
196  
-            def get_redirect_url(self, pk):
  197
+            def get_redirect_url(self, *args, **kwargs):
197 198
                 article = get_object_or_404(Article, pk=pk)
198 199
                 article.update_counter()
199  
-                return reverse('product_detail', args=(pk,))
  200
+                return super(ArticleCounterRedirectView, self).get_redirect_url(*args, **kwargs)
200 201
 
201 202
     **Example urls.py**::
202 203
 
203 204
         from django.conf.urls import patterns, url
204 205
         from django.views.generic.base import RedirectView
205 206
 
206  
-        from article.views import ArticleCounterRedirectView
  207
+        from article.views import ArticleCounterRedirectView, ArticleDetail
207 208
 
208 209
         urlpatterns = patterns('',
209 210
 
210  
-            url(r'^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
  211
+            url(r'^counter/(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
  212
+            url(r'^details/(?P<pk>\d+)/$', ArticleDetail.as_view(), name='article-detail'),
211 213
             url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
212 214
         )
213 215
 
@@ -218,6 +220,11 @@ RedirectView
218 220
         The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
219 221
         HTTP error.
220 222
 
  223
+    .. attribute:: pattern_name
  224
+
  225
+        The name of the URL pattern to redirect to. Reversing will be done
  226
+        using the same args and kwargs as are passed in for this view.
  227
+
221 228
     .. attribute:: permanent
222 229
 
223 230
         Whether the redirect should be permanent. The only difference here is
3  docs/releases/1.6.txt
@@ -688,6 +688,9 @@ Miscellaneous
688 688
 
689 689
     url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete')
690 690
 
  691
+* :class:`~django.views.generic.base.RedirectView` now has a `pattern_name`
  692
+  attribute which allows it to choose the target by reversing the URL.
  693
+
691 694
 Features deprecated in 1.6
692 695
 ==========================
693 696
 
20  tests/generic_views/test_base.py
@@ -317,7 +317,9 @@ def test_content_type(self):
317 317
         self.assertEqual(response['Content-Type'], 'text/plain')
318 318
 
319 319
 
320  
-class RedirectViewTest(unittest.TestCase):
  320
+class RedirectViewTest(TestCase):
  321
+    urls = 'generic_views.urls'
  322
+
321 323
     rf = RequestFactory()
322 324
 
323 325
     def test_no_url(self):
@@ -360,6 +362,22 @@ def test_parameter_substitution(self):
360 362
         self.assertEqual(response.status_code, 301)
361 363
         self.assertEqual(response.url, '/bar/42/')
362 364
 
  365
+    def test_named_url_pattern(self):
  366
+        "Named pattern parameter should reverse to the matching pattern"
  367
+        response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), pk=1)
  368
+        self.assertEqual(response.status_code, 301)
  369
+        self.assertEqual(response['Location'], '/detail/artist/1/')
  370
+
  371
+    def test_named_url_pattern_using_args(self):
  372
+        response = RedirectView.as_view(pattern_name='artist_detail')(self.rf.get('/foo/'), 1)
  373
+        self.assertEqual(response.status_code, 301)
  374
+        self.assertEqual(response['Location'], '/detail/artist/1/')
  375
+
  376
+    def test_wrong_named_url_pattern(self):
  377
+        "A wrong pattern name returns 410 GONE"
  378
+        response = RedirectView.as_view(pattern_name='wrong.pattern_name')(self.rf.get('/foo/'))
  379
+        self.assertEqual(response.status_code, 410)
  380
+
363 381
     def test_redirect_POST(self):
364 382
         "Default is a permanent redirect"
365 383
         response = RedirectView.as_view(url='/bar/')(self.rf.post('/foo/'))

0 notes on commit b7bd708

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