mirror of
https://github.com/django/django.git
synced 2025-04-01 03:56:42 +00:00
Fixed #14611 -- Added query_params argument to RequestFactory and Client classes.
This commit is contained in:
parent
e76cc93b01
commit
a03593967f
@ -381,13 +381,22 @@ class RequestFactory:
|
|||||||
just as if that view had been hooked up using a URLconf.
|
just as if that view had been hooked up using a URLconf.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *, json_encoder=DjangoJSONEncoder, headers=None, **defaults):
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
json_encoder=DjangoJSONEncoder,
|
||||||
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
|
**defaults,
|
||||||
|
):
|
||||||
self.json_encoder = json_encoder
|
self.json_encoder = json_encoder
|
||||||
self.defaults = defaults
|
self.defaults = defaults
|
||||||
self.cookies = SimpleCookie()
|
self.cookies = SimpleCookie()
|
||||||
self.errors = BytesIO()
|
self.errors = BytesIO()
|
||||||
if headers:
|
if headers:
|
||||||
self.defaults.update(HttpHeaders.to_wsgi_names(headers))
|
self.defaults.update(HttpHeaders.to_wsgi_names(headers))
|
||||||
|
if query_params:
|
||||||
|
self.defaults["QUERY_STRING"] = urlencode(query_params, doseq=True)
|
||||||
|
|
||||||
def _base_environ(self, **request):
|
def _base_environ(self, **request):
|
||||||
"""
|
"""
|
||||||
@ -459,18 +468,21 @@ class RequestFactory:
|
|||||||
# Refs comment in `get_bytes_from_wsgi()`.
|
# Refs comment in `get_bytes_from_wsgi()`.
|
||||||
return path.decode("iso-8859-1")
|
return path.decode("iso-8859-1")
|
||||||
|
|
||||||
def get(self, path, data=None, secure=False, *, headers=None, **extra):
|
def get(
|
||||||
|
self, path, data=None, secure=False, *, headers=None, query_params=None, **extra
|
||||||
|
):
|
||||||
"""Construct a GET request."""
|
"""Construct a GET request."""
|
||||||
data = {} if data is None else data
|
if query_params and data:
|
||||||
|
raise ValueError("query_params and data arguments are mutually exclusive.")
|
||||||
|
query_params = data or query_params
|
||||||
|
query_params = {} if query_params is None else query_params
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"GET",
|
"GET",
|
||||||
path,
|
path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
**{
|
query_params=query_params,
|
||||||
"QUERY_STRING": urlencode(data, doseq=True),
|
|
||||||
**extra,
|
**extra,
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(
|
def post(
|
||||||
@ -481,6 +493,7 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct a POST request."""
|
"""Construct a POST request."""
|
||||||
@ -494,26 +507,37 @@ class RequestFactory:
|
|||||||
content_type,
|
content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def head(self, path, data=None, secure=False, *, headers=None, **extra):
|
def head(
|
||||||
|
self, path, data=None, secure=False, *, headers=None, query_params=None, **extra
|
||||||
|
):
|
||||||
"""Construct a HEAD request."""
|
"""Construct a HEAD request."""
|
||||||
data = {} if data is None else data
|
if query_params and data:
|
||||||
|
raise ValueError("query_params and data arguments are mutually exclusive.")
|
||||||
|
query_params = data or query_params
|
||||||
|
query_params = {} if query_params is None else query_params
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"HEAD",
|
"HEAD",
|
||||||
path,
|
path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
**{
|
query_params=query_params,
|
||||||
"QUERY_STRING": urlencode(data, doseq=True),
|
|
||||||
**extra,
|
**extra,
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def trace(self, path, secure=False, *, headers=None, **extra):
|
def trace(self, path, secure=False, *, headers=None, query_params=None, **extra):
|
||||||
"""Construct a TRACE request."""
|
"""Construct a TRACE request."""
|
||||||
return self.generic("TRACE", path, secure=secure, headers=headers, **extra)
|
return self.generic(
|
||||||
|
"TRACE",
|
||||||
|
path,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
|
)
|
||||||
|
|
||||||
def options(
|
def options(
|
||||||
self,
|
self,
|
||||||
@ -523,11 +547,19 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"Construct an OPTIONS request."
|
"Construct an OPTIONS request."
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"OPTIONS", path, data, content_type, secure=secure, headers=headers, **extra
|
"OPTIONS",
|
||||||
|
path,
|
||||||
|
data,
|
||||||
|
content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def put(
|
def put(
|
||||||
@ -538,12 +570,20 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct a PUT request."""
|
"""Construct a PUT request."""
|
||||||
data = self._encode_json(data, content_type)
|
data = self._encode_json(data, content_type)
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"PUT", path, data, content_type, secure=secure, headers=headers, **extra
|
"PUT",
|
||||||
|
path,
|
||||||
|
data,
|
||||||
|
content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def patch(
|
def patch(
|
||||||
@ -554,12 +594,20 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct a PATCH request."""
|
"""Construct a PATCH request."""
|
||||||
data = self._encode_json(data, content_type)
|
data = self._encode_json(data, content_type)
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"PATCH", path, data, content_type, secure=secure, headers=headers, **extra
|
"PATCH",
|
||||||
|
path,
|
||||||
|
data,
|
||||||
|
content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def delete(
|
def delete(
|
||||||
@ -570,12 +618,20 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct a DELETE request."""
|
"""Construct a DELETE request."""
|
||||||
data = self._encode_json(data, content_type)
|
data = self._encode_json(data, content_type)
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"DELETE", path, data, content_type, secure=secure, headers=headers, **extra
|
"DELETE",
|
||||||
|
path,
|
||||||
|
data,
|
||||||
|
content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def generic(
|
def generic(
|
||||||
@ -587,6 +643,7 @@ class RequestFactory:
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct an arbitrary HTTP request."""
|
"""Construct an arbitrary HTTP request."""
|
||||||
@ -608,6 +665,8 @@ class RequestFactory:
|
|||||||
)
|
)
|
||||||
if headers:
|
if headers:
|
||||||
extra.update(HttpHeaders.to_wsgi_names(headers))
|
extra.update(HttpHeaders.to_wsgi_names(headers))
|
||||||
|
if query_params:
|
||||||
|
extra["QUERY_STRING"] = urlencode(query_params, doseq=True)
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
# If QUERY_STRING is absent or empty, we want to extract it from the URL.
|
# If QUERY_STRING is absent or empty, we want to extract it from the URL.
|
||||||
if not r.get("QUERY_STRING"):
|
if not r.get("QUERY_STRING"):
|
||||||
@ -685,6 +744,7 @@ class AsyncRequestFactory(RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct an arbitrary HTTP request."""
|
"""Construct an arbitrary HTTP request."""
|
||||||
@ -705,18 +765,20 @@ class AsyncRequestFactory(RequestFactory):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
s["_body_file"] = FakePayload(data)
|
s["_body_file"] = FakePayload(data)
|
||||||
if query_string := extra.pop("QUERY_STRING", None):
|
if query_params:
|
||||||
|
s["query_string"] = urlencode(query_params, doseq=True)
|
||||||
|
elif query_string := extra.pop("QUERY_STRING", None):
|
||||||
s["query_string"] = query_string
|
s["query_string"] = query_string
|
||||||
|
else:
|
||||||
|
# If QUERY_STRING is absent or empty, we want to extract it from
|
||||||
|
# the URL.
|
||||||
|
s["query_string"] = parsed[4]
|
||||||
if headers:
|
if headers:
|
||||||
extra.update(HttpHeaders.to_asgi_names(headers))
|
extra.update(HttpHeaders.to_asgi_names(headers))
|
||||||
s["headers"] += [
|
s["headers"] += [
|
||||||
(key.lower().encode("ascii"), value.encode("latin1"))
|
(key.lower().encode("ascii"), value.encode("latin1"))
|
||||||
for key, value in extra.items()
|
for key, value in extra.items()
|
||||||
]
|
]
|
||||||
# If QUERY_STRING is absent or empty, we want to extract it from the
|
|
||||||
# URL.
|
|
||||||
if not s.get("query_string"):
|
|
||||||
s["query_string"] = parsed[4]
|
|
||||||
return self.request(**s)
|
return self.request(**s)
|
||||||
|
|
||||||
|
|
||||||
@ -889,7 +951,14 @@ class ClientMixin:
|
|||||||
return response._json
|
return response._json
|
||||||
|
|
||||||
def _follow_redirect(
|
def _follow_redirect(
|
||||||
self, response, *, data="", content_type="", headers=None, **extra
|
self,
|
||||||
|
response,
|
||||||
|
*,
|
||||||
|
data="",
|
||||||
|
content_type="",
|
||||||
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
|
**extra,
|
||||||
):
|
):
|
||||||
"""Follow a single redirect contained in response using GET."""
|
"""Follow a single redirect contained in response using GET."""
|
||||||
response_url = response.url
|
response_url = response.url
|
||||||
@ -934,6 +1003,7 @@ class ClientMixin:
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
follow=False,
|
follow=False,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -978,9 +1048,10 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
raise_request_exception=True,
|
raise_request_exception=True,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**defaults,
|
**defaults,
|
||||||
):
|
):
|
||||||
super().__init__(headers=headers, **defaults)
|
super().__init__(headers=headers, query_params=query_params, **defaults)
|
||||||
self.handler = ClientHandler(enforce_csrf_checks)
|
self.handler = ClientHandler(enforce_csrf_checks)
|
||||||
self.raise_request_exception = raise_request_exception
|
self.raise_request_exception = raise_request_exception
|
||||||
self.exc_info = None
|
self.exc_info = None
|
||||||
@ -1042,15 +1113,23 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using GET."""
|
"""Request a response from the server using GET."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = super().get(path, data=data, secure=secure, headers=headers, **extra)
|
response = super().get(
|
||||||
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1063,6 +1142,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using POST."""
|
"""Request a response from the server using POST."""
|
||||||
@ -1074,11 +1154,17 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1090,17 +1176,23 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using HEAD."""
|
"""Request a response from the server using HEAD."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = super().head(
|
response = super().head(
|
||||||
path, data=data, secure=secure, headers=headers, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1113,6 +1205,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using OPTIONS."""
|
"""Request a response from the server using OPTIONS."""
|
||||||
@ -1124,11 +1217,17 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1141,6 +1240,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PUT."""
|
"""Send a resource to the server using PUT."""
|
||||||
@ -1152,11 +1252,17 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1169,6 +1275,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PATCH."""
|
"""Send a resource to the server using PATCH."""
|
||||||
@ -1180,11 +1287,17 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1197,6 +1310,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a DELETE request to the server."""
|
"""Send a DELETE request to the server."""
|
||||||
@ -1208,11 +1322,17 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1224,17 +1344,23 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a TRACE request to the server."""
|
"""Send a TRACE request to the server."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = super().trace(
|
response = super().trace(
|
||||||
path, data=data, secure=secure, headers=headers, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1244,6 +1370,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
data="",
|
data="",
|
||||||
content_type="",
|
content_type="",
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -1257,6 +1384,7 @@ class Client(ClientMixin, RequestFactory):
|
|||||||
data=data,
|
data=data,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
response.redirect_chain = redirect_chain
|
response.redirect_chain = redirect_chain
|
||||||
@ -1278,9 +1406,10 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
raise_request_exception=True,
|
raise_request_exception=True,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**defaults,
|
**defaults,
|
||||||
):
|
):
|
||||||
super().__init__(headers=headers, **defaults)
|
super().__init__(headers=headers, query_params=query_params, **defaults)
|
||||||
self.handler = AsyncClientHandler(enforce_csrf_checks)
|
self.handler = AsyncClientHandler(enforce_csrf_checks)
|
||||||
self.raise_request_exception = raise_request_exception
|
self.raise_request_exception = raise_request_exception
|
||||||
self.exc_info = None
|
self.exc_info = None
|
||||||
@ -1341,17 +1470,23 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using GET."""
|
"""Request a response from the server using GET."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = await super().get(
|
response = await super().get(
|
||||||
path, data=data, secure=secure, headers=headers, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1364,6 +1499,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using POST."""
|
"""Request a response from the server using POST."""
|
||||||
@ -1375,11 +1511,17 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1391,17 +1533,23 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using HEAD."""
|
"""Request a response from the server using HEAD."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = await super().head(
|
response = await super().head(
|
||||||
path, data=data, secure=secure, headers=headers, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1414,6 +1562,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using OPTIONS."""
|
"""Request a response from the server using OPTIONS."""
|
||||||
@ -1425,11 +1574,17 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1442,6 +1597,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PUT."""
|
"""Send a resource to the server using PUT."""
|
||||||
@ -1453,11 +1609,17 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1470,6 +1632,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PATCH."""
|
"""Send a resource to the server using PATCH."""
|
||||||
@ -1481,11 +1644,17 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1498,6 +1667,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a DELETE request to the server."""
|
"""Send a DELETE request to the server."""
|
||||||
@ -1509,11 +1679,17 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, content_type=content_type, headers=headers, **extra
|
response,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1525,17 +1701,23 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
secure=False,
|
secure=False,
|
||||||
*,
|
*,
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a TRACE request to the server."""
|
"""Send a TRACE request to the server."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
response = await super().trace(
|
response = await super().trace(
|
||||||
path, data=data, secure=secure, headers=headers, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = await self._ahandle_redirects(
|
response = await self._ahandle_redirects(
|
||||||
response, data=data, headers=headers, **extra
|
response, data=data, headers=headers, query_params=query_params, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -1545,6 +1727,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
data="",
|
data="",
|
||||||
content_type="",
|
content_type="",
|
||||||
headers=None,
|
headers=None,
|
||||||
|
query_params=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -1558,6 +1741,7 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
|||||||
data=data,
|
data=data,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
|
query_params=query_params,
|
||||||
**extra,
|
**extra,
|
||||||
)
|
)
|
||||||
response.redirect_chain = redirect_chain
|
response.redirect_chain = redirect_chain
|
||||||
|
@ -224,6 +224,17 @@ Tests
|
|||||||
* The Django test runner now supports a ``--screenshots`` option to save
|
* The Django test runner now supports a ``--screenshots`` option to save
|
||||||
screenshots for Selenium tests.
|
screenshots for Selenium tests.
|
||||||
|
|
||||||
|
* The :class:`~django.test.RequestFactory`,
|
||||||
|
:class:`~django.test.AsyncRequestFactory`, :class:`~django.test.Client`, and
|
||||||
|
:class:`~django.test.AsyncClient` classes now support the ``query_params``
|
||||||
|
parameter, which accepts a dictionary of query string keys and values. This
|
||||||
|
allows setting query strings on any HTTP methods more easily.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
self.client.post("/items/1", query_params={"action": "delete"})
|
||||||
|
await self.async_client.post("/items/1", query_params={"action": "delete"})
|
||||||
|
|
||||||
URLs
|
URLs
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
|
@ -32,6 +32,10 @@ restricted subset of the test client API:
|
|||||||
attributes must be supplied by the test itself if required
|
attributes must be supplied by the test itself if required
|
||||||
for the view to function properly.
|
for the view to function properly.
|
||||||
|
|
||||||
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` parameter was added.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -85,6 +89,10 @@ difference being that it returns ``ASGIRequest`` instances rather than
|
|||||||
Arbitrary keyword arguments in ``defaults`` are added directly into the ASGI
|
Arbitrary keyword arguments in ``defaults`` are added directly into the ASGI
|
||||||
scope.
|
scope.
|
||||||
|
|
||||||
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` parameter was added.
|
||||||
|
|
||||||
Testing class-based views
|
Testing class-based views
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ Making requests
|
|||||||
|
|
||||||
Use the ``django.test.Client`` class to make requests.
|
Use the ``django.test.Client`` class to make requests.
|
||||||
|
|
||||||
.. class:: Client(enforce_csrf_checks=False, raise_request_exception=True, json_encoder=DjangoJSONEncoder, *, headers=None, **defaults)
|
.. class:: Client(enforce_csrf_checks=False, raise_request_exception=True, json_encoder=DjangoJSONEncoder, *, headers=None, query_params=None, **defaults)
|
||||||
|
|
||||||
A testing HTTP client. Takes several arguments that can customize behavior.
|
A testing HTTP client. Takes several arguments that can customize behavior.
|
||||||
|
|
||||||
@ -129,6 +129,9 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
|
|
||||||
client = Client(headers={"user-agent": "curl/7.79.1"})
|
client = Client(headers={"user-agent": "curl/7.79.1"})
|
||||||
|
|
||||||
|
``query_params`` allows you to specify the default query string that will
|
||||||
|
be set on every request.
|
||||||
|
|
||||||
Arbitrary keyword arguments in ``**defaults`` set WSGI
|
Arbitrary keyword arguments in ``**defaults`` set WSGI
|
||||||
:pep:`environ variables <3333#environ-variables>`. For example, to set the
|
:pep:`environ variables <3333#environ-variables>`. For example, to set the
|
||||||
script name::
|
script name::
|
||||||
@ -140,8 +143,8 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
Keyword arguments starting with a ``HTTP_`` prefix are set as headers,
|
Keyword arguments starting with a ``HTTP_`` prefix are set as headers,
|
||||||
but the ``headers`` parameter should be preferred for readability.
|
but the ``headers`` parameter should be preferred for readability.
|
||||||
|
|
||||||
The values from the ``headers`` and ``extra`` keyword arguments passed to
|
The values from the ``headers``, ``query_params``, and ``extra`` keyword
|
||||||
:meth:`~django.test.Client.get()`,
|
arguments passed to :meth:`~django.test.Client.get()`,
|
||||||
:meth:`~django.test.Client.post()`, etc. have precedence over
|
:meth:`~django.test.Client.post()`, etc. have precedence over
|
||||||
the defaults passed to the class constructor.
|
the defaults passed to the class constructor.
|
||||||
|
|
||||||
@ -155,21 +158,25 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
The ``json_encoder`` argument allows setting a custom JSON encoder for
|
The ``json_encoder`` argument allows setting a custom JSON encoder for
|
||||||
the JSON serialization that's described in :meth:`post`.
|
the JSON serialization that's described in :meth:`post`.
|
||||||
|
|
||||||
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
Once you have a ``Client`` instance, you can call any of the following
|
Once you have a ``Client`` instance, you can call any of the following
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
.. method:: Client.get(path, data=None, follow=False, secure=False, *, headers=None, **extra)
|
.. method:: Client.get(path, data=None, follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a GET request on the provided ``path`` and returns a ``Response``
|
Makes a GET request on the provided ``path`` and returns a ``Response``
|
||||||
object, which is documented below.
|
object, which is documented below.
|
||||||
|
|
||||||
The key-value pairs in the ``data`` dictionary are used to create a GET
|
The key-value pairs in the ``query_params`` dictionary are used to set
|
||||||
data payload. For example:
|
query strings. For example:
|
||||||
|
|
||||||
.. code-block:: pycon
|
.. code-block:: pycon
|
||||||
|
|
||||||
>>> c = Client()
|
>>> c = Client()
|
||||||
>>> c.get("/customers/details/", {"name": "fred", "age": 7})
|
>>> c.get("/customers/details/", query_params={"name": "fred", "age": 7})
|
||||||
|
|
||||||
...will result in the evaluation of a GET request equivalent to:
|
...will result in the evaluation of a GET request equivalent to:
|
||||||
|
|
||||||
@ -177,6 +184,10 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
|
|
||||||
/customers/details/?name=fred&age=7
|
/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
|
It is also possible to pass these parameters into the ``data``
|
||||||
|
parameter. However, ``query_params`` is preferred as it works for any
|
||||||
|
HTTP method.
|
||||||
|
|
||||||
The ``headers`` parameter can be used to specify headers to be sent in
|
The ``headers`` parameter can be used to specify headers to be sent in
|
||||||
the request. For example:
|
the request. For example:
|
||||||
|
|
||||||
@ -185,7 +196,7 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
>>> c = Client()
|
>>> c = Client()
|
||||||
>>> c.get(
|
>>> c.get(
|
||||||
... "/customers/details/",
|
... "/customers/details/",
|
||||||
... {"name": "fred", "age": 7},
|
... query_params={"name": "fred", "age": 7},
|
||||||
... headers={"accept": "application/json"},
|
... headers={"accept": "application/json"},
|
||||||
... )
|
... )
|
||||||
|
|
||||||
@ -211,8 +222,8 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
>>> c = Client()
|
>>> c = Client()
|
||||||
>>> c.get("/customers/details/?name=fred&age=7")
|
>>> c.get("/customers/details/?name=fred&age=7")
|
||||||
|
|
||||||
If you provide a URL with both an encoded GET data and a data argument,
|
If you provide a URL with both an encoded GET data and either a
|
||||||
the data argument will take precedence.
|
query_params or data argument these arguments will take precedence.
|
||||||
|
|
||||||
If you set ``follow`` to ``True`` the client will follow any redirects
|
If you set ``follow`` to ``True`` the client will follow any redirects
|
||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
@ -230,7 +241,11 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
||||||
request.
|
request.
|
||||||
|
|
||||||
.. method:: Client.post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a POST request on the provided ``path`` and returns a
|
Makes a POST request on the provided ``path`` and returns a
|
||||||
``Response`` object, which is documented below.
|
``Response`` object, which is documented below.
|
||||||
@ -321,8 +336,8 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
such as an image, this means you will need to open the file in
|
such as an image, this means you will need to open the file in
|
||||||
``rb`` (read binary) mode.
|
``rb`` (read binary) mode.
|
||||||
|
|
||||||
The ``headers`` and ``extra`` parameters acts the same as for
|
The ``headers``, ``query_params``, and ``extra`` parameters acts the
|
||||||
:meth:`Client.get`.
|
same as for :meth:`Client.get`.
|
||||||
|
|
||||||
If the URL you request with a POST contains encoded parameters, these
|
If the URL you request with a POST contains encoded parameters, these
|
||||||
parameters will be made available in the request.GET data. For example,
|
parameters will be made available in the request.GET data. For example,
|
||||||
@ -330,7 +345,9 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
|
|
||||||
.. code-block:: pycon
|
.. code-block:: pycon
|
||||||
|
|
||||||
>>> c.post("/login/?visitor=true", {"name": "fred", "passwd": "secret"})
|
>>> c.post(
|
||||||
|
... "/login/", {"name": "fred", "passwd": "secret"}, query_params={"visitor": "true"}
|
||||||
|
... )
|
||||||
|
|
||||||
... the view handling this request could interrogate request.POST
|
... the view handling this request could interrogate request.POST
|
||||||
to retrieve the username and password, and could interrogate request.GET
|
to retrieve the username and password, and could interrogate request.GET
|
||||||
@ -343,14 +360,22 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
||||||
request.
|
request.
|
||||||
|
|
||||||
.. method:: Client.head(path, data=None, follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.head(path, data=None, follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a HEAD request on the provided ``path`` and returns a
|
Makes a HEAD request on the provided ``path`` and returns a
|
||||||
``Response`` object. This method works just like :meth:`Client.get`,
|
``Response`` object. This method works just like :meth:`Client.get`,
|
||||||
including the ``follow``, ``secure``, ``headers``, and ``extra``
|
including the ``follow``, ``secure``, ``headers``, ``query_params``,
|
||||||
parameters, except it does not return a message body.
|
and ``extra`` parameters, except it does not return a message body.
|
||||||
|
|
||||||
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes an OPTIONS request on the provided ``path`` and returns a
|
Makes an OPTIONS request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
@ -358,10 +383,14 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
The ``follow``, ``secure``, ``headers``, ``query_params``, and
|
||||||
the same as for :meth:`Client.get`.
|
``extra`` parameters act the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a PUT request on the provided ``path`` and returns a
|
Makes a PUT request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
@ -369,18 +398,26 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
The ``follow``, ``secure``, ``headers``, ``query_params``, and
|
||||||
the same as for :meth:`Client.get`.
|
``extra`` parameters act the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a PATCH request on the provided ``path`` and returns a
|
Makes a PATCH request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
|
|
||||||
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
The ``follow``, ``secure``, ``headers``, ``query_params``, and
|
||||||
the same as for :meth:`Client.get`.
|
``extra`` parameters act the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a DELETE request on the provided ``path`` and returns a
|
Makes a DELETE request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
@ -388,10 +425,14 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
The ``follow``, ``secure``, ``headers``, ``query_params``, and
|
||||||
the same as for :meth:`Client.get`.
|
``extra`` parameters act the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.trace(path, follow=False, secure=False, *, headers=None, **extra)
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
|
.. method:: Client.trace(path, follow=False, secure=False, *, headers=None, query_params=None, **extra)
|
||||||
|
|
||||||
Makes a TRACE request on the provided ``path`` and returns a
|
Makes a TRACE request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for simulating diagnostic probes.
|
``Response`` object. Useful for simulating diagnostic probes.
|
||||||
@ -400,8 +441,12 @@ Use the ``django.test.Client`` class to make requests.
|
|||||||
parameter in order to comply with :rfc:`9110#section-9.3.8`, which
|
parameter in order to comply with :rfc:`9110#section-9.3.8`, which
|
||||||
mandates that TRACE requests must not have a body.
|
mandates that TRACE requests must not have a body.
|
||||||
|
|
||||||
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
The ``follow``, ``secure``, ``headers``, ``query_params``, and
|
||||||
the same as for :meth:`Client.get`.
|
``extra`` parameters act the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
.. method:: Client.login(**credentials)
|
.. method:: Client.login(**credentials)
|
||||||
.. method:: Client.alogin(**credentials)
|
.. method:: Client.alogin(**credentials)
|
||||||
@ -1997,7 +2042,7 @@ If you are testing from an asynchronous function, you must also use the
|
|||||||
asynchronous test client. This is available as ``django.test.AsyncClient``,
|
asynchronous test client. This is available as ``django.test.AsyncClient``,
|
||||||
or as ``self.async_client`` on any test.
|
or as ``self.async_client`` on any test.
|
||||||
|
|
||||||
.. class:: AsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, **defaults)
|
.. class:: AsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=None, query_params=None, **defaults)
|
||||||
|
|
||||||
``AsyncClient`` has the same methods and signatures as the synchronous (normal)
|
``AsyncClient`` has the same methods and signatures as the synchronous (normal)
|
||||||
test client, with the following exceptions:
|
test client, with the following exceptions:
|
||||||
@ -2017,6 +2062,10 @@ test client, with the following exceptions:
|
|||||||
|
|
||||||
Support for the ``follow`` parameter was added to the ``AsyncClient``.
|
Support for the ``follow`` parameter was added to the ``AsyncClient``.
|
||||||
|
|
||||||
|
.. versionchanged:: 5.1
|
||||||
|
|
||||||
|
The ``query_params`` argument was added.
|
||||||
|
|
||||||
Using ``AsyncClient`` any method that makes a request must be awaited::
|
Using ``AsyncClient`` any method that makes a request must be awaited::
|
||||||
|
|
||||||
async def test_my_thing(self):
|
async def test_my_thing(self):
|
||||||
|
@ -1002,6 +1002,36 @@ class ClientTest(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(response.content, b"named_temp_file")
|
self.assertEqual(response.content, b"named_temp_file")
|
||||||
|
|
||||||
|
def test_query_params(self):
|
||||||
|
tests = (
|
||||||
|
"get",
|
||||||
|
"post",
|
||||||
|
"put",
|
||||||
|
"patch",
|
||||||
|
"delete",
|
||||||
|
"head",
|
||||||
|
"options",
|
||||||
|
"trace",
|
||||||
|
)
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
client_method = getattr(self.client, method)
|
||||||
|
response = client_method("/get_view/", query_params={"example": "data"})
|
||||||
|
self.assertEqual(response.wsgi_request.GET["example"], "data")
|
||||||
|
|
||||||
|
def test_cannot_use_data_and_query_params_together(self):
|
||||||
|
tests = ["get", "head"]
|
||||||
|
msg = "query_params and data arguments are mutually exclusive."
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
client_method = getattr(self.client, method)
|
||||||
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
client_method(
|
||||||
|
"/get_view/",
|
||||||
|
data={"example": "data"},
|
||||||
|
query_params={"q": "terms"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"],
|
MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"],
|
||||||
@ -1127,6 +1157,23 @@ class RequestFactoryTest(SimpleTestCase):
|
|||||||
self.assertEqual(request.headers["x-another-header"], "some other value")
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
|
def test_request_factory_query_params(self):
|
||||||
|
tests = (
|
||||||
|
"get",
|
||||||
|
"post",
|
||||||
|
"put",
|
||||||
|
"patch",
|
||||||
|
"delete",
|
||||||
|
"head",
|
||||||
|
"options",
|
||||||
|
"trace",
|
||||||
|
)
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
factory = getattr(self.request_factory, method)
|
||||||
|
request = factory("/somewhere", query_params={"example": "data"})
|
||||||
|
self.assertEqual(request.GET["example"], "data")
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF="test_client.urls")
|
@override_settings(ROOT_URLCONF="test_client.urls")
|
||||||
class AsyncClientTest(TestCase):
|
class AsyncClientTest(TestCase):
|
||||||
@ -1183,6 +1230,25 @@ class AsyncClientTest(TestCase):
|
|||||||
response = await self.async_client.get("/post_view/")
|
response = await self.async_client.get("/post_view/")
|
||||||
self.assertContains(response, "Viewing GET page.")
|
self.assertContains(response, "Viewing GET page.")
|
||||||
|
|
||||||
|
async def test_query_params(self):
|
||||||
|
tests = (
|
||||||
|
"get",
|
||||||
|
"post",
|
||||||
|
"put",
|
||||||
|
"patch",
|
||||||
|
"delete",
|
||||||
|
"head",
|
||||||
|
"options",
|
||||||
|
"trace",
|
||||||
|
)
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
client_method = getattr(self.async_client, method)
|
||||||
|
response = await client_method(
|
||||||
|
"/async_get_view/", query_params={"example": "data"}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.asgi_request.GET["example"], "data")
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF="test_client.urls")
|
@override_settings(ROOT_URLCONF="test_client.urls")
|
||||||
class AsyncRequestFactoryTest(SimpleTestCase):
|
class AsyncRequestFactoryTest(SimpleTestCase):
|
||||||
@ -1264,3 +1330,33 @@ class AsyncRequestFactoryTest(SimpleTestCase):
|
|||||||
request = self.request_factory.get("/somewhere/", {"example": "data"})
|
request = self.request_factory.get("/somewhere/", {"example": "data"})
|
||||||
self.assertNotIn("Query-String", request.headers)
|
self.assertNotIn("Query-String", request.headers)
|
||||||
self.assertEqual(request.GET["example"], "data")
|
self.assertEqual(request.GET["example"], "data")
|
||||||
|
|
||||||
|
def test_request_factory_query_params(self):
|
||||||
|
tests = (
|
||||||
|
"get",
|
||||||
|
"post",
|
||||||
|
"put",
|
||||||
|
"patch",
|
||||||
|
"delete",
|
||||||
|
"head",
|
||||||
|
"options",
|
||||||
|
"trace",
|
||||||
|
)
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
factory = getattr(self.request_factory, method)
|
||||||
|
request = factory("/somewhere", query_params={"example": "data"})
|
||||||
|
self.assertEqual(request.GET["example"], "data")
|
||||||
|
|
||||||
|
def test_cannot_use_data_and_query_params_together(self):
|
||||||
|
tests = ["get", "head"]
|
||||||
|
msg = "query_params and data arguments are mutually exclusive."
|
||||||
|
for method in tests:
|
||||||
|
with self.subTest(method=method):
|
||||||
|
factory = getattr(self.request_factory, method)
|
||||||
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
factory(
|
||||||
|
"/somewhere",
|
||||||
|
data={"example": "data"},
|
||||||
|
query_params={"q": "terms"},
|
||||||
|
)
|
||||||
|
@ -1197,6 +1197,10 @@ class QueryStringTests(SimpleTestCase):
|
|||||||
self.assertEqual(response.context["get-foo"], "whiz")
|
self.assertEqual(response.context["get-foo"], "whiz")
|
||||||
self.assertIsNone(response.context["post-foo"])
|
self.assertIsNone(response.context["post-foo"])
|
||||||
|
|
||||||
|
response = self.client.post("/request_data/", query_params={"foo": "whiz"})
|
||||||
|
self.assertEqual(response.context["get-foo"], "whiz")
|
||||||
|
self.assertIsNone(response.context["post-foo"])
|
||||||
|
|
||||||
# POST data provided in the URL augments actual form data
|
# POST data provided in the URL augments actual form data
|
||||||
response = self.client.post("/request_data/?foo=whiz", data={"foo": "bang"})
|
response = self.client.post("/request_data/?foo=whiz", data={"foo": "bang"})
|
||||||
self.assertEqual(response.context["get-foo"], "whiz")
|
self.assertEqual(response.context["get-foo"], "whiz")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user