Commit b7bd7087 authored by Marc Tamlyn's avatar Marc Tamlyn
Browse files

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

Thanks to @DrMeers for the report and @ludwigkraatz for the initial patch.
parent 8365ed08
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from functools import update_wrapper

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 @@ class RedirectView(View):
        """
        if self.url:
            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', '')
        if args and self.query_string:
            url = "%s?%s" % (url, args)
        return url
        else:
            return None

    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)
+11 −4
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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
==========================

+19 −1
Original line number Diff line number Diff line
@@ -317,7 +317,9 @@ class TemplateViewTest(TestCase):
        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 @@ class RedirectViewTest(unittest.TestCase):
        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/'))