mirror of https://github.com/django/django.git
Fixed #34074 -- Added headers argument to RequestFactory and Client classes.
This commit is contained in:
parent
b181cae2e3
commit
67da22f08e
|
@ -1,5 +1,6 @@
|
||||||
from django.http.cookie import SimpleCookie, parse_cookie
|
from django.http.cookie import SimpleCookie, parse_cookie
|
||||||
from django.http.request import (
|
from django.http.request import (
|
||||||
|
HttpHeaders,
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
QueryDict,
|
QueryDict,
|
||||||
RawPostDataException,
|
RawPostDataException,
|
||||||
|
@ -27,6 +28,7 @@ from django.http.response import (
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"SimpleCookie",
|
"SimpleCookie",
|
||||||
"parse_cookie",
|
"parse_cookie",
|
||||||
|
"HttpHeaders",
|
||||||
"HttpRequest",
|
"HttpRequest",
|
||||||
"QueryDict",
|
"QueryDict",
|
||||||
"RawPostDataException",
|
"RawPostDataException",
|
||||||
|
|
|
@ -461,6 +461,31 @@ class HttpHeaders(CaseInsensitiveMapping):
|
||||||
return None
|
return None
|
||||||
return header.replace("_", "-").title()
|
return header.replace("_", "-").title()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_wsgi_name(cls, header):
|
||||||
|
header = header.replace("-", "_").upper()
|
||||||
|
if header in cls.UNPREFIXED_HEADERS:
|
||||||
|
return header
|
||||||
|
return f"{cls.HTTP_PREFIX}{header}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_asgi_name(cls, header):
|
||||||
|
return header.replace("-", "_").upper()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_wsgi_names(cls, headers):
|
||||||
|
return {
|
||||||
|
cls.to_wsgi_name(header_name): value
|
||||||
|
for header_name, value in headers.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_asgi_names(cls, headers):
|
||||||
|
return {
|
||||||
|
cls.to_asgi_name(header_name): value
|
||||||
|
for header_name, value in headers.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class QueryDict(MultiValueDict):
|
class QueryDict(MultiValueDict):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,8 +11,7 @@ from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import DisallowedHost, ImproperlyConfigured
|
from django.core.exceptions import DisallowedHost, ImproperlyConfigured
|
||||||
from django.http import UnreadablePostError
|
from django.http import HttpHeaders, UnreadablePostError
|
||||||
from django.http.request import HttpHeaders
|
|
||||||
from django.urls import get_callable
|
from django.urls import get_callable
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils.crypto import constant_time_compare, get_random_string
|
from django.utils.crypto import constant_time_compare, get_random_string
|
||||||
|
|
|
@ -18,7 +18,7 @@ from django.core.handlers.wsgi import LimitedStream, WSGIRequest
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.core.signals import got_request_exception, request_finished, request_started
|
from django.core.signals import got_request_exception, request_finished, request_started
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections
|
||||||
from django.http import HttpRequest, QueryDict, SimpleCookie
|
from django.http import HttpHeaders, HttpRequest, QueryDict, SimpleCookie
|
||||||
from django.test import signals
|
from django.test import signals
|
||||||
from django.test.utils import ContextList
|
from django.test.utils import ContextList
|
||||||
from django.urls import resolve
|
from django.urls import resolve
|
||||||
|
@ -346,11 +346,13 @@ 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, **defaults):
|
def __init__(self, *, json_encoder=DjangoJSONEncoder, headers=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:
|
||||||
|
self.defaults.update(HttpHeaders.to_wsgi_names(headers))
|
||||||
|
|
||||||
def _base_environ(self, **request):
|
def _base_environ(self, **request):
|
||||||
"""
|
"""
|
||||||
|
@ -422,13 +424,14 @@ 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, **extra):
|
def get(self, path, data=None, secure=False, *, headers=None, **extra):
|
||||||
"""Construct a GET request."""
|
"""Construct a GET request."""
|
||||||
data = {} if data is None else data
|
data = {} if data is None else data
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"GET",
|
"GET",
|
||||||
path,
|
path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
**{
|
**{
|
||||||
"QUERY_STRING": urlencode(data, doseq=True),
|
"QUERY_STRING": urlencode(data, doseq=True),
|
||||||
**extra,
|
**extra,
|
||||||
|
@ -436,32 +439,46 @@ class RequestFactory:
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(
|
def post(
|
||||||
self, path, data=None, content_type=MULTIPART_CONTENT, secure=False, **extra
|
self,
|
||||||
|
path,
|
||||||
|
data=None,
|
||||||
|
content_type=MULTIPART_CONTENT,
|
||||||
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct a POST request."""
|
"""Construct a POST request."""
|
||||||
data = self._encode_json({} if data is None else data, content_type)
|
data = self._encode_json({} if data is None else data, content_type)
|
||||||
post_data = self._encode_data(data, content_type)
|
post_data = self._encode_data(data, content_type)
|
||||||
|
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"POST", path, post_data, content_type, secure=secure, **extra
|
"POST",
|
||||||
|
path,
|
||||||
|
post_data,
|
||||||
|
content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
|
|
||||||
def head(self, path, data=None, secure=False, **extra):
|
def head(self, path, data=None, secure=False, *, headers=None, **extra):
|
||||||
"""Construct a HEAD request."""
|
"""Construct a HEAD request."""
|
||||||
data = {} if data is None else data
|
data = {} if data is None else data
|
||||||
return self.generic(
|
return self.generic(
|
||||||
"HEAD",
|
"HEAD",
|
||||||
path,
|
path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
**{
|
**{
|
||||||
"QUERY_STRING": urlencode(data, doseq=True),
|
"QUERY_STRING": urlencode(data, doseq=True),
|
||||||
**extra,
|
**extra,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def trace(self, path, secure=False, **extra):
|
def trace(self, path, secure=False, *, headers=None, **extra):
|
||||||
"""Construct a TRACE request."""
|
"""Construct a TRACE request."""
|
||||||
return self.generic("TRACE", path, secure=secure, **extra)
|
return self.generic("TRACE", path, secure=secure, headers=headers, **extra)
|
||||||
|
|
||||||
def options(
|
def options(
|
||||||
self,
|
self,
|
||||||
|
@ -469,10 +486,14 @@ class RequestFactory:
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"Construct an OPTIONS request."
|
"Construct an OPTIONS request."
|
||||||
return self.generic("OPTIONS", path, data, content_type, secure=secure, **extra)
|
return self.generic(
|
||||||
|
"OPTIONS", path, data, content_type, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
|
|
||||||
def put(
|
def put(
|
||||||
self,
|
self,
|
||||||
|
@ -480,11 +501,15 @@ class RequestFactory:
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=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("PUT", path, data, content_type, secure=secure, **extra)
|
return self.generic(
|
||||||
|
"PUT", path, data, content_type, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
|
|
||||||
def patch(
|
def patch(
|
||||||
self,
|
self,
|
||||||
|
@ -492,11 +517,15 @@ class RequestFactory:
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=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("PATCH", path, data, content_type, secure=secure, **extra)
|
return self.generic(
|
||||||
|
"PATCH", path, data, content_type, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
|
|
||||||
def delete(
|
def delete(
|
||||||
self,
|
self,
|
||||||
|
@ -504,11 +533,15 @@ class RequestFactory:
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=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("DELETE", path, data, content_type, secure=secure, **extra)
|
return self.generic(
|
||||||
|
"DELETE", path, data, content_type, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
|
|
||||||
def generic(
|
def generic(
|
||||||
self,
|
self,
|
||||||
|
@ -517,6 +550,8 @@ class RequestFactory:
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct an arbitrary HTTP request."""
|
"""Construct an arbitrary HTTP request."""
|
||||||
|
@ -536,6 +571,8 @@ class RequestFactory:
|
||||||
"wsgi.input": FakePayload(data),
|
"wsgi.input": FakePayload(data),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if headers:
|
||||||
|
extra.update(HttpHeaders.to_wsgi_names(headers))
|
||||||
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"):
|
||||||
|
@ -611,6 +648,8 @@ class AsyncRequestFactory(RequestFactory):
|
||||||
data="",
|
data="",
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Construct an arbitrary HTTP request."""
|
"""Construct an arbitrary HTTP request."""
|
||||||
|
@ -636,6 +675,8 @@ class AsyncRequestFactory(RequestFactory):
|
||||||
s["follow"] = follow
|
s["follow"] = follow
|
||||||
if query_string := extra.pop("QUERY_STRING", None):
|
if query_string := extra.pop("QUERY_STRING", None):
|
||||||
s["query_string"] = query_string
|
s["query_string"] = query_string
|
||||||
|
if 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()
|
||||||
|
@ -782,9 +823,14 @@ class Client(ClientMixin, RequestFactory):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, enforce_csrf_checks=False, raise_request_exception=True, **defaults
|
self,
|
||||||
|
enforce_csrf_checks=False,
|
||||||
|
raise_request_exception=True,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**defaults,
|
||||||
):
|
):
|
||||||
super().__init__(**defaults)
|
super().__init__(headers=headers, **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
|
||||||
|
@ -837,12 +883,23 @@ class Client(ClientMixin, RequestFactory):
|
||||||
self.cookies.update(response.cookies)
|
self.cookies.update(response.cookies)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get(self, path, data=None, follow=False, secure=False, **extra):
|
def get(
|
||||||
|
self,
|
||||||
|
path,
|
||||||
|
data=None,
|
||||||
|
follow=False,
|
||||||
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**extra,
|
||||||
|
):
|
||||||
"""Request a response from the server using GET."""
|
"""Request a response from the server using GET."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().get(path, data=data, secure=secure, **extra)
|
response = super().get(path, data=data, secure=secure, headers=headers, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, data=data, **extra)
|
response = self._handle_redirects(
|
||||||
|
response, data=data, headers=headers, **extra
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def post(
|
def post(
|
||||||
|
@ -852,25 +909,45 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type=MULTIPART_CONTENT,
|
content_type=MULTIPART_CONTENT,
|
||||||
follow=False,
|
follow=False,
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using POST."""
|
"""Request a response from the server using POST."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().post(
|
response = super().post(
|
||||||
path, data=data, content_type=content_type, secure=secure, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, **extra
|
response, data=data, content_type=content_type, headers=headers, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def head(self, path, data=None, follow=False, secure=False, **extra):
|
def head(
|
||||||
|
self,
|
||||||
|
path,
|
||||||
|
data=None,
|
||||||
|
follow=False,
|
||||||
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**extra,
|
||||||
|
):
|
||||||
"""Request a response from the server using HEAD."""
|
"""Request a response from the server using HEAD."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().head(path, data=data, secure=secure, **extra)
|
response = super().head(
|
||||||
|
path, data=data, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, data=data, **extra)
|
response = self._handle_redirects(
|
||||||
|
response, data=data, headers=headers, **extra
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def options(
|
def options(
|
||||||
|
@ -880,16 +957,23 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
follow=False,
|
follow=False,
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Request a response from the server using OPTIONS."""
|
"""Request a response from the server using OPTIONS."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().options(
|
response = super().options(
|
||||||
path, data=data, content_type=content_type, secure=secure, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, **extra
|
response, data=data, content_type=content_type, headers=headers, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -900,16 +984,23 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
follow=False,
|
follow=False,
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PUT."""
|
"""Send a resource to the server using PUT."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().put(
|
response = super().put(
|
||||||
path, data=data, content_type=content_type, secure=secure, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, **extra
|
response, data=data, content_type=content_type, headers=headers, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -920,16 +1011,23 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
follow=False,
|
follow=False,
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a resource to the server using PATCH."""
|
"""Send a resource to the server using PATCH."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().patch(
|
response = super().patch(
|
||||||
path, data=data, content_type=content_type, secure=secure, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, **extra
|
response, data=data, content_type=content_type, headers=headers, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -940,28 +1038,55 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type="application/octet-stream",
|
content_type="application/octet-stream",
|
||||||
follow=False,
|
follow=False,
|
||||||
secure=False,
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
**extra,
|
**extra,
|
||||||
):
|
):
|
||||||
"""Send a DELETE request to the server."""
|
"""Send a DELETE request to the server."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().delete(
|
response = super().delete(
|
||||||
path, data=data, content_type=content_type, secure=secure, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(
|
response = self._handle_redirects(
|
||||||
response, data=data, content_type=content_type, **extra
|
response, data=data, content_type=content_type, headers=headers, **extra
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def trace(self, path, data="", follow=False, secure=False, **extra):
|
def trace(
|
||||||
|
self,
|
||||||
|
path,
|
||||||
|
data="",
|
||||||
|
follow=False,
|
||||||
|
secure=False,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**extra,
|
||||||
|
):
|
||||||
"""Send a TRACE request to the server."""
|
"""Send a TRACE request to the server."""
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
response = super().trace(path, data=data, secure=secure, **extra)
|
response = super().trace(
|
||||||
|
path, data=data, secure=secure, headers=headers, **extra
|
||||||
|
)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, data=data, **extra)
|
response = self._handle_redirects(
|
||||||
|
response, data=data, headers=headers, **extra
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _handle_redirects(self, response, data="", content_type="", **extra):
|
def _handle_redirects(
|
||||||
|
self,
|
||||||
|
response,
|
||||||
|
data="",
|
||||||
|
content_type="",
|
||||||
|
headers=None,
|
||||||
|
**extra,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Follow any redirects by requesting responses from the server using GET.
|
Follow any redirects by requesting responses from the server using GET.
|
||||||
"""
|
"""
|
||||||
|
@ -1010,7 +1135,12 @@ class Client(ClientMixin, RequestFactory):
|
||||||
content_type = None
|
content_type = None
|
||||||
|
|
||||||
response = request_method(
|
response = request_method(
|
||||||
path, data=data, content_type=content_type, follow=False, **extra
|
path,
|
||||||
|
data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
follow=False,
|
||||||
|
headers=headers,
|
||||||
|
**extra,
|
||||||
)
|
)
|
||||||
response.redirect_chain = redirect_chain
|
response.redirect_chain = redirect_chain
|
||||||
|
|
||||||
|
@ -1038,9 +1168,14 @@ class AsyncClient(ClientMixin, AsyncRequestFactory):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, enforce_csrf_checks=False, raise_request_exception=True, **defaults
|
self,
|
||||||
|
enforce_csrf_checks=False,
|
||||||
|
raise_request_exception=True,
|
||||||
|
*,
|
||||||
|
headers=None,
|
||||||
|
**defaults,
|
||||||
):
|
):
|
||||||
super().__init__(**defaults)
|
super().__init__(headers=headers, **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
|
||||||
|
|
|
@ -279,6 +279,22 @@ Tests
|
||||||
* The :option:`test --debug-sql` option now formats SQL queries with
|
* The :option:`test --debug-sql` option now formats SQL queries with
|
||||||
``sqlparse``.
|
``sqlparse``.
|
||||||
|
|
||||||
|
* The :class:`~django.test.RequestFactory`,
|
||||||
|
:class:`~django.test.AsyncRequestFactory`, :class:`~django.test.Client`, and
|
||||||
|
:class:`~django.test.AsyncClient` classes now support the ``headers``
|
||||||
|
parameter, which accepts a dictionary of header names and values. This allows
|
||||||
|
a more natural syntax for declaring headers.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Before:
|
||||||
|
self.client.get("/home/", HTTP_ACCEPT_LANGUAGE="fr")
|
||||||
|
await self.async_client.get("/home/", ACCEPT_LANGUAGE="fr")
|
||||||
|
|
||||||
|
# After:
|
||||||
|
self.client.get("/home/", headers={"accept-language": "fr"})
|
||||||
|
await self.async_client.get("/home/", headers={"accept-language": "fr"})
|
||||||
|
|
||||||
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:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -83,6 +87,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:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
Testing class-based views
|
Testing class-based views
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -112,15 +112,27 @@ 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, **defaults)
|
.. class:: Client(enforce_csrf_checks=False, raise_request_exception=True, json_encoder=DjangoJSONEncoder, *, headers=None, **defaults)
|
||||||
|
|
||||||
It requires no arguments at time of construction. However, you can use
|
A testing HTTP client. Takes several arguments that can customize behavior.
|
||||||
keyword arguments to specify some default headers. For example, this will
|
|
||||||
send a ``User-Agent`` HTTP header in each request::
|
|
||||||
|
|
||||||
>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
|
``headers`` allows you to specify default headers that will be sent with
|
||||||
|
every request. For example, to set a ``User-Agent`` header::
|
||||||
|
|
||||||
The values from the ``extra`` keyword arguments passed to
|
client = Client(headers={"user-agent": "curl/7.79.1"})
|
||||||
|
|
||||||
|
Arbitrary keyword arguments in ``**defaults`` set WSGI
|
||||||
|
:pep:`environ variables <3333#environ-variables>`. For example, to set the
|
||||||
|
script name::
|
||||||
|
|
||||||
|
client = Client(SCRIPT_NAME="/app/")
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Keyword arguments starting with a ``HTTP_`` prefix are set as headers,
|
||||||
|
but the ``headers`` parameter should be preferred for readability.
|
||||||
|
|
||||||
|
The values from the ``headers`` and ``extra`` keyword arguments passed to
|
||||||
:meth:`~django.test.Client.get()`,
|
: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.
|
||||||
|
@ -138,7 +150,11 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
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, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.get(path, data=None, follow=False, secure=False, *, headers=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.
|
||||||
|
@ -153,25 +169,23 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
|
|
||||||
/customers/details/?name=fred&age=7
|
/customers/details/?name=fred&age=7
|
||||||
|
|
||||||
The ``extra`` keyword arguments parameter can be used to specify
|
The ``headers`` parameter can be used to specify headers to be sent in
|
||||||
headers to be sent in the request. For example::
|
the request. For example::
|
||||||
|
|
||||||
>>> c = Client()
|
>>> c = Client()
|
||||||
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
|
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
|
||||||
... HTTP_ACCEPT='application/json')
|
... headers={'accept': 'application/json'})
|
||||||
|
|
||||||
...will send the HTTP header ``HTTP_ACCEPT`` to the details view, which
|
...will send the HTTP header ``HTTP_ACCEPT`` to the details view, which
|
||||||
is a good way to test code paths that use the
|
is a good way to test code paths that use the
|
||||||
:meth:`django.http.HttpRequest.accepts()` method.
|
:meth:`django.http.HttpRequest.accepts()` method.
|
||||||
|
|
||||||
.. admonition:: CGI specification
|
Arbitrary keyword arguments set WSGI
|
||||||
|
:pep:`environ variables <3333#environ-variables>`. For example, headers
|
||||||
|
to set the script name::
|
||||||
|
|
||||||
The headers sent via ``**extra`` should follow CGI_ specification.
|
>>> c = Client()
|
||||||
For example, emulating a different "Host" header as sent in the
|
>>> c.get("/", SCRIPT_NAME="/app/")
|
||||||
HTTP request from the browser to the server should be passed
|
|
||||||
as ``HTTP_HOST``.
|
|
||||||
|
|
||||||
.. _CGI: https://www.w3.org/CGI/
|
|
||||||
|
|
||||||
If you already have the GET arguments in URL-encoded form, you can
|
If you already have the GET arguments in URL-encoded form, you can
|
||||||
use that encoding instead of using the data argument. For example,
|
use that encoding instead of using the data argument. For example,
|
||||||
|
@ -197,7 +211,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, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, *, headers=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.
|
||||||
|
@ -277,7 +295,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 ``extra`` argument acts the same as for :meth:`Client.get`.
|
The ``headers`` and ``extra`` parameters acts the 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,
|
||||||
|
@ -296,14 +315,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, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.head(path, data=None, follow=False, secure=False, *, headers=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`` and ``extra`` arguments, except
|
including the ``follow``, ``secure``, ``headers``, and ``extra``
|
||||||
it does not return a message body.
|
parameters, except it does not return a message body.
|
||||||
|
|
||||||
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=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.
|
||||||
|
@ -311,10 +338,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`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
||||||
:meth:`Client.get`.
|
the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=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.
|
||||||
|
@ -322,18 +353,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`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
||||||
:meth:`Client.get`.
|
the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=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`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
||||||
:meth:`Client.get`.
|
the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, *, headers=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.
|
||||||
|
@ -341,10 +380,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`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
||||||
:meth:`Client.get`.
|
the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.trace(path, follow=False, secure=False, **extra)
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
|
.. method:: Client.trace(path, follow=False, secure=False, *, headers=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.
|
||||||
|
@ -353,8 +396,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``, and ``extra`` arguments act the same as for
|
The ``follow``, ``secure``, ``headers``, and ``extra`` parameters act
|
||||||
:meth:`Client.get`.
|
the same as for :meth:`Client.get`.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter was added.
|
||||||
|
|
||||||
.. method:: Client.login(**credentials)
|
.. method:: Client.login(**credentials)
|
||||||
|
|
||||||
|
@ -1905,7 +1952,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, **defaults)
|
.. class:: AsyncClient(enforce_csrf_checks=False, raise_request_exception=True, *, headers=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 two exceptions:
|
test client, with two exceptions:
|
||||||
|
@ -1924,6 +1971,10 @@ test client, with two exceptions:
|
||||||
... ACCEPT='application/json'
|
... ACCEPT='application/json'
|
||||||
... )
|
... )
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
The ``headers`` parameter 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):
|
||||||
|
|
|
@ -2139,7 +2139,7 @@ class UnprefixedDefaultLanguageTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_unprefixed_language_with_accept_language(self):
|
def test_unprefixed_language_with_accept_language(self):
|
||||||
"""'Accept-Language' is respected."""
|
"""'Accept-Language' is respected."""
|
||||||
response = self.client.get("/simple/", HTTP_ACCEPT_LANGUAGE="fr")
|
response = self.client.get("/simple/", headers={"accept-language": "fr"})
|
||||||
self.assertRedirects(response, "/fr/simple/")
|
self.assertRedirects(response, "/fr/simple/")
|
||||||
|
|
||||||
def test_unprefixed_language_with_cookie_language(self):
|
def test_unprefixed_language_with_cookie_language(self):
|
||||||
|
@ -2149,7 +2149,7 @@ class UnprefixedDefaultLanguageTests(SimpleTestCase):
|
||||||
self.assertRedirects(response, "/fr/simple/")
|
self.assertRedirects(response, "/fr/simple/")
|
||||||
|
|
||||||
def test_unprefixed_language_with_non_valid_language(self):
|
def test_unprefixed_language_with_non_valid_language(self):
|
||||||
response = self.client.get("/simple/", HTTP_ACCEPT_LANGUAGE="fi")
|
response = self.client.get("/simple/", headers={"accept-language": "fi"})
|
||||||
self.assertEqual(response.content, b"Yes")
|
self.assertEqual(response.content, b"Yes")
|
||||||
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fi"})
|
self.client.cookies.load({settings.LANGUAGE_COOKIE_NAME: "fi"})
|
||||||
response = self.client.get("/simple/")
|
response = self.client.get("/simple/")
|
||||||
|
|
|
@ -5,9 +5,14 @@ from urllib.parse import urlencode
|
||||||
|
|
||||||
from django.core.exceptions import DisallowedHost
|
from django.core.exceptions import DisallowedHost
|
||||||
from django.core.handlers.wsgi import LimitedStream, WSGIRequest
|
from django.core.handlers.wsgi import LimitedStream, WSGIRequest
|
||||||
from django.http import HttpRequest, RawPostDataException, UnreadablePostError
|
from django.http import (
|
||||||
|
HttpHeaders,
|
||||||
|
HttpRequest,
|
||||||
|
RawPostDataException,
|
||||||
|
UnreadablePostError,
|
||||||
|
)
|
||||||
from django.http.multipartparser import MultiPartParserError
|
from django.http.multipartparser import MultiPartParserError
|
||||||
from django.http.request import HttpHeaders, split_domain_port
|
from django.http.request import split_domain_port
|
||||||
from django.test import RequestFactory, SimpleTestCase, override_settings
|
from django.test import RequestFactory, SimpleTestCase, override_settings
|
||||||
from django.test.client import FakePayload
|
from django.test.client import FakePayload
|
||||||
|
|
||||||
|
|
|
@ -1066,6 +1066,52 @@ class RequestFactoryTest(SimpleTestCase):
|
||||||
echoed_request_line = "TRACE {} {}".format(url_path, protocol)
|
echoed_request_line = "TRACE {} {}".format(url_path, protocol)
|
||||||
self.assertContains(response, echoed_request_line)
|
self.assertContains(response, echoed_request_line)
|
||||||
|
|
||||||
|
def test_request_factory_default_headers(self):
|
||||||
|
request = RequestFactory(
|
||||||
|
HTTP_AUTHORIZATION="Bearer faketoken",
|
||||||
|
HTTP_X_ANOTHER_HEADER="some other value",
|
||||||
|
).get("/somewhere/")
|
||||||
|
self.assertEqual(request.headers["authorization"], "Bearer faketoken")
|
||||||
|
self.assertIn("HTTP_AUTHORIZATION", request.META)
|
||||||
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
|
request = RequestFactory(
|
||||||
|
headers={
|
||||||
|
"Authorization": "Bearer faketoken",
|
||||||
|
"X-Another-Header": "some other value",
|
||||||
|
}
|
||||||
|
).get("/somewhere/")
|
||||||
|
self.assertEqual(request.headers["authorization"], "Bearer faketoken")
|
||||||
|
self.assertIn("HTTP_AUTHORIZATION", request.META)
|
||||||
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
|
def test_request_factory_sets_headers(self):
|
||||||
|
for method_name, view in self.http_methods_and_views:
|
||||||
|
method = getattr(self.request_factory, method_name)
|
||||||
|
request = method(
|
||||||
|
"/somewhere/",
|
||||||
|
HTTP_AUTHORIZATION="Bearer faketoken",
|
||||||
|
HTTP_X_ANOTHER_HEADER="some other value",
|
||||||
|
)
|
||||||
|
self.assertEqual(request.headers["authorization"], "Bearer faketoken")
|
||||||
|
self.assertIn("HTTP_AUTHORIZATION", request.META)
|
||||||
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
|
request = method(
|
||||||
|
"/somewhere/",
|
||||||
|
headers={
|
||||||
|
"Authorization": "Bearer faketoken",
|
||||||
|
"X-Another-Header": "some other value",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertEqual(request.headers["authorization"], "Bearer faketoken")
|
||||||
|
self.assertIn("HTTP_AUTHORIZATION", request.META)
|
||||||
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF="test_client.urls")
|
@override_settings(ROOT_URLCONF="test_client.urls")
|
||||||
class AsyncClientTest(TestCase):
|
class AsyncClientTest(TestCase):
|
||||||
|
@ -1176,6 +1222,18 @@ class AsyncRequestFactoryTest(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)
|
||||||
|
|
||||||
|
request = self.request_factory.get(
|
||||||
|
"/somewhere/",
|
||||||
|
headers={
|
||||||
|
"Authorization": "Bearer faketoken",
|
||||||
|
"X-Another-Header": "some other value",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assertEqual(request.headers["authorization"], "Bearer faketoken")
|
||||||
|
self.assertIn("HTTP_AUTHORIZATION", request.META)
|
||||||
|
self.assertEqual(request.headers["x-another-header"], "some other value")
|
||||||
|
self.assertIn("HTTP_X_ANOTHER_HEADER", request.META)
|
||||||
|
|
||||||
def test_request_factory_query_string(self):
|
def test_request_factory_query_string(self):
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue