Skip to content

Commit 8b52dc0

Browse files
Do not treat missing non-form data as empty dict
This allows views to distinguish missing payload from empty payload. Related: #3647, #4566
1 parent 24a938a commit 8b52dc0

File tree

6 files changed

+33
-4
lines changed

6 files changed

+33
-4
lines changed

docs/api-guide/requests.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ REST framework's Request objects provide flexible request parsing that allows yo
2424
* It includes all parsed content, including *file and non-file* inputs.
2525
* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
2626
* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming [JSON data] similarly to how you handle incoming [form data].
27+
* If the client does not send any data and does not specify form encoding, the value of `.data` is determined by the `DEFAULT_MISSING_DATA` setting. If form encoding is used and no data is sent, `.data` will be an empty Django `QueryDict`.
2728

2829
For more details see the [parsers documentation].
2930

docs/api-guide/settings.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ A view inspector class that will be used for schema generation.
103103

104104
Default: `'rest_framework.schemas.openapi.AutoSchema'`
105105

106+
#### DEFAULT_MISSING_DATA
107+
108+
The value that should be used for `request.data` when the client did not send any data in the request body. This
109+
setting applies only if the client did not send a header indicating form encoding.
110+
111+
Default: `None`
112+
106113
---
107114

108115
## Generic view settings

rest_framework/request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ def _parse(self):
341341
if media_type and is_form_media_type(media_type):
342342
empty_data = QueryDict('', encoding=self._request._encoding)
343343
else:
344-
empty_data = {}
344+
empty_data = api_settings.DEFAULT_MISSING_DATA
345345
empty_files = MultiValueDict()
346346
return (empty_data, empty_files)
347347

rest_framework/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
4747
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
4848
'DEFAULT_VERSIONING_CLASS': None,
49+
'DEFAULT_MISSING_DATA': None,
4950

5051
# Generic view behavior
5152
'DEFAULT_PAGINATION_CLASS': None,

tests/test_request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@ def test_standard_behaviour_determines_no_content_GET(self):
5656
Ensure request.data returns empty QueryDict for GET request.
5757
"""
5858
request = Request(factory.get('/'))
59-
assert request.data == {}
59+
assert request.data is None
6060

6161
def test_standard_behaviour_determines_no_content_HEAD(self):
6262
"""
6363
Ensure request.data returns empty QueryDict for HEAD request.
6464
"""
6565
request = Request(factory.head('/'))
66-
assert request.data == {}
66+
assert request.data is None
6767

6868
def test_request_DATA_with_form_content(self):
6969
"""

tests/test_testing.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class BasicSerializer(serializers.Serializer):
4242
@api_view(['POST'])
4343
def post_view(request):
4444
serializer = BasicSerializer(data=request.data)
45+
serializer.allow_null = ('allow_null' in request.query_params)
4546
serializer.is_valid(raise_exception=True)
4647
return Response(serializer.validated_data)
4748

@@ -191,7 +192,26 @@ def test_invalid_multipart_data(self):
191192
path='/view/', data={'valid': 123, 'invalid': {'a': 123}}
192193
)
193194

194-
def test_empty_post_uses_default_boolean_value(self):
195+
def test_missing_post_payload_causes_400(self):
196+
response = self.client.post(
197+
'/post-view/',
198+
data=None,
199+
content_type='application/json'
200+
)
201+
assert response.status_code == 400
202+
assert response.data['non_field_errors'] == ['No data provided']
203+
204+
def test_missing_post_payload_allow_null_causes_200(self):
205+
response = self.client.post(
206+
'/post-view/?allow_null=1',
207+
data=None,
208+
content_type='application/json'
209+
)
210+
assert response.status_code == 200
211+
assert response.data is None
212+
213+
@override_settings(REST_FRAMEWORK={'DEFAULT_MISSING_DATA': {}})
214+
def test_missing_post_payload_coerced_dict_uses_default_boolean_value(self):
195215
response = self.client.post(
196216
'/post-view/',
197217
data=None,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy