diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 649d871cf738..f06c0ab8663a 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -235,6 +235,7 @@ class RedirectView(View): url = None pattern_name = None query_string = False + preserve_request = False def get_redirect_url(self, *args, **kwargs): """ @@ -261,9 +262,11 @@ def get(self, request, *args, **kwargs): url = self.get_redirect_url(*args, **kwargs) if url: if self.permanent: - return HttpResponsePermanentRedirect(url) + return HttpResponsePermanentRedirect( + url, preserve_request=self.preserve_request + ) else: - return HttpResponseRedirect(url) + return HttpResponseRedirect(url, preserve_request=self.preserve_request) else: response = HttpResponseGone() log_response("Gone: %s", request.path, response=response, request=request) diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt index 887a83b37ffc..2d745b5e98d6 100644 --- a/docs/ref/class-based-views/base.txt +++ b/docs/ref/class-based-views/base.txt @@ -277,6 +277,15 @@ ancestor classes are documented under the section title of **Ancestors then the query string is discarded. By default, ``query_string`` is ``False``. + .. attribute:: preserve_request + + .. versionadded:: 6.1 + + Whether to preserve the HTTP method and body during the redirect. If + ``True``, then the redirect will use status code 307 instead of 302, + or 308 instead of 301 when :attr:`permanent` is ``True``. By default, + ``preserve_request`` is ``False``. + **Methods** .. method:: get_redirect_url(*args, **kwargs) diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index dd2a193589e3..8f7172256e2a 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -334,7 +334,9 @@ Forms Generic Views ~~~~~~~~~~~~~ -* ... +* The new :attr:`.RedirectView.preserve_request` attribute allows preserving + the HTTP method and body during redirects, using 307/308 status codes instead + of 302/301. Internationalization ~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index 9d6cef8a4644..b1179ebe61ff 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -479,6 +479,20 @@ def test_temporary_redirect(self): self.assertEqual(response.status_code, 302) self.assertEqual(response.url, "/bar/") + def test_preserve_request_temporary_redirect(self): + response = RedirectView.as_view(url="/bar/", preserve_request=True)( + self.rf.get("/foo/") + ) + self.assertEqual(response.status_code, 307) + self.assertEqual(response.url, "/bar/") + + def test_preserve_request_permanent_redirect(self): + response = RedirectView.as_view( + url="/bar/", preserve_request=True, permanent=True + )(self.rf.get("/foo/")) + self.assertEqual(response.status_code, 308) + self.assertEqual(response.url, "/bar/") + def test_include_args(self): "GET arguments can be included in the redirected URL" response = RedirectView.as_view(url="/bar/")(self.rf.get("/foo/")) diff --git a/tests/requirements/py3.txt b/tests/requirements/py3.txt index 7b60635b9252..cf89b65b3a38 100644 --- a/tests/requirements/py3.txt +++ b/tests/requirements/py3.txt @@ -14,7 +14,7 @@ pymemcache >= 3.4.0 pywatchman; sys_platform != 'win32' PyYAML >= 6.0.2 redis >= 5.1.0 -selenium >= 4.23.0 +selenium >= 4.23.0,<4.44.0 sqlparse >= 0.5.0 tblib >= 3.0.0 tzdata