diff --git a/django/test/client.py b/django/test/client.py index b26504f762..ce74a5fe53 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -826,8 +826,12 @@ class Client(ClientMixin, RequestFactory): path = urljoin(response.request['PATH_INFO'], path) if response.status_code in (HTTPStatus.TEMPORARY_REDIRECT, HTTPStatus.PERMANENT_REDIRECT): - # Preserve request method post-redirect for 307/308 responses. - request_method = getattr(self, response.request['REQUEST_METHOD'].lower()) + # Preserve request method and query string (if needed) + # post-redirect for 307/308 responses. + request_method = response.request['REQUEST_METHOD'].lower() + if request_method not in ('get', 'head'): + extra['QUERY_STRING'] = url.query + request_method = getattr(self, request_method) else: request_method = self.get data = QueryDict(url.query) diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt index 2d49299440..84f757cd2b 100644 --- a/docs/releases/3.2.txt +++ b/docs/releases/3.2.txt @@ -214,6 +214,9 @@ Tests creating deep copies with :py:func:`copy.deepcopy`. Assigning objects which don't support ``deepcopy()`` is deprecated and will be removed in Django 4.1. +* :class:`~django.test.Client` now preserves the request query string when + following 307 and 308 redirects. + URLs ~~~~ diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index 0b63a3fed5..03bb658952 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -284,6 +284,20 @@ class ClientTest(TestCase): self.assertEqual(response.request['PATH_INFO'], '/post_view/') self.assertEqual(response.request['REQUEST_METHOD'], method.upper()) + def test_follow_307_and_308_preserves_query_string(self): + methods = ('post', 'options', 'put', 'patch', 'delete', 'trace') + codes = (307, 308) + for method, code in itertools.product(methods, codes): + with self.subTest(method=method, code=code): + req_method = getattr(self.client, method) + response = req_method( + '/redirect_view_%s_query_string/' % code, + data={'value': 'test'}, + follow=True, + ) + self.assertRedirects(response, '/post_view/?hello=world', status_code=code) + self.assertEqual(response.request['QUERY_STRING'], 'hello=world') + def test_follow_307_and_308_get_head_query_string(self): methods = ('get', 'head') codes = (307, 308)