diff --git a/django/views/generic/simple.py b/django/views/generic/simple.py index 3b5309df96..435cd7623d 100644 --- a/django/views/generic/simple.py +++ b/django/views/generic/simple.py @@ -17,7 +17,7 @@ def direct_to_template(request, template, extra_context=None, mimetype=None, **k t = loader.get_template(template) return HttpResponse(t.render(c), mimetype=mimetype) -def redirect_to(request, url, permanent=True, **kwargs): +def redirect_to(request, url, permanent=True, query_string=False, **kwargs): """ Redirect to a given URL. @@ -33,7 +33,15 @@ def redirect_to(request, url, permanent=True, **kwargs): If the ``permanent`` argument is False, then the response will have a 302 HTTP status code. Otherwise, the status code will be 301. + + If the ``query_string`` argument is True, then the GET query string + from the request is appended to the URL. + """ + args = request.META["QUERY_STRING"] + if args and query_string and url is not None: + url = "%s?%s" % (url, args) + if url is not None: klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect return klass(url % kwargs) diff --git a/docs/ref/generic-views.txt b/docs/ref/generic-views.txt index 65f0d2eb30..a7d67c74e3 100644 --- a/docs/ref/generic-views.txt +++ b/docs/ref/generic-views.txt @@ -88,9 +88,15 @@ If the given URL is ``None``, Django will return an ``HttpResponseGone`` (410). redirect will use status code 301. If ``False``, then the redirect will use status code 302. By default, ``permanent`` is ``True``. + * ``query_string``: Whether to pass along the GET query string to + the new location. If ``True``, then the query string is appended + to the URL. If ``False``, then the query string is discarded. By + default, ``query_string`` is ``False``. + .. versionadded:: 1.1 The ``permanent`` keyword argument is new in Django 1.1. + **Example:** This example issues a permanent redirect (HTTP status code 301) from diff --git a/tests/regressiontests/views/tests/__init__.py b/tests/regressiontests/views/tests/__init__.py index 697968ee52..edd533e175 100644 --- a/tests/regressiontests/views/tests/__init__.py +++ b/tests/regressiontests/views/tests/__init__.py @@ -2,6 +2,7 @@ from debug import * from defaults import * from generic.create_update import * from generic.date_based import * +from generic.simple import * from i18n import * from specials import * from static import * diff --git a/tests/regressiontests/views/tests/generic/simple.py b/tests/regressiontests/views/tests/generic/simple.py new file mode 100644 index 0000000000..f94b3da439 --- /dev/null +++ b/tests/regressiontests/views/tests/generic/simple.py @@ -0,0 +1,38 @@ +# coding: utf-8 + +from django.test import TestCase + +class RedirectToTest(TestCase): + def test_redirect_to_returns_permanent_redirect(self): + "simple.redirect_to returns a permanent redirect (301) by default" + response = self.client.get('/views/simple/redirect_to/') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + + def test_redirect_to_can_return_a_temporary_redirect(self): + "simple.redirect_to returns a temporary redirect (302) when explicitely asked to" + response = self.client.get('/views/simple/redirect_to_temp/') + self.assertEqual(response.status_code, 302) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + + def test_redirect_to_on_empty_url_returns_gone(self): + "simple.redirect_to returns resource gone (410) when given a None url" + response = self.client.get('/views/simple/redirect_to_none/') + self.assertEqual(response.status_code, 410) + + def test_redirect_to_allows_formatted_url_string(self): + "simple.redirect_to uses string interpolation on target url for keyword args" + response = self.client.get('/views/simple/redirect_to_arg/42/') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target_arg/42/', response['Location']) + + def test_redirect_to_allows_query_string_to_be_passed(self): + "simple.redirect_to configured with query_string=True passes on any query string" + # the default is to not forward the query string + response = self.client.get('/views/simple/redirect_to/?param1=foo¶m2=bar') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/', response['Location']) + # views configured with query_string=True however passes the query string along + response = self.client.get('/views/simple/redirect_to_query/?param1=foo¶m2=bar') + self.assertEqual(response.status_code, 301) + self.assertEqual('http://testserver/views/simple/target/?param1=foo¶m2=bar', response['Location']) diff --git a/tests/regressiontests/views/urls.py b/tests/regressiontests/views/urls.py index f5675d0e28..b42700baeb 100644 --- a/tests/regressiontests/views/urls.py +++ b/tests/regressiontests/views/urls.py @@ -77,7 +77,6 @@ urlpatterns += patterns('django.views.generic.date_based', ) # crud generic views. - urlpatterns += patterns('django.views.generic.create_update', (r'^create_update/member/create/article/$', 'create_object', dict(login_required=True, model=Article)), @@ -123,3 +122,12 @@ urlpatterns += patterns('regressiontests.views.views', url(r'view_exception/(?P\d+)/$', 'view_exception', name='view_exception'), url(r'template_exception/(?P\d+)/$', 'template_exception', name='template_exception'), ) + +# simple generic views. +urlpatterns += patterns('django.views.generic.simple', + (r'^simple/redirect_to/$', 'redirect_to', dict(url='/views/simple/target/')), + (r'^simple/redirect_to_temp/$', 'redirect_to', dict(url='/views/simple/target/', permanent=False)), + (r'^simple/redirect_to_none/$', 'redirect_to', dict(url=None)), + (r'^simple/redirect_to_arg/(?P\d+)/$', 'redirect_to', dict(url='/views/simple/target_arg/%(id)s/')), + (r'^simple/redirect_to_query/$', 'redirect_to', dict(url='/views/simple/target/', query_string=True)), +)