mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-14 05:36:50 +03:00
Do not treat missing non-form data as empty dict
This allows views to distinguish missing payload from empty payload. Related: #3647, #4566
This commit is contained in:
parent
24a938abaa
commit
8b52dc09ee
|
@ -24,6 +24,7 @@ REST framework's Request objects provide flexible request parsing that allows yo
|
||||||
* It includes all parsed content, including *file and non-file* inputs.
|
* It includes all parsed content, including *file and non-file* inputs.
|
||||||
* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
|
* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
|
||||||
* 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].
|
* 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].
|
||||||
|
* 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`.
|
||||||
|
|
||||||
For more details see the [parsers documentation].
|
For more details see the [parsers documentation].
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,13 @@ A view inspector class that will be used for schema generation.
|
||||||
|
|
||||||
Default: `'rest_framework.schemas.openapi.AutoSchema'`
|
Default: `'rest_framework.schemas.openapi.AutoSchema'`
|
||||||
|
|
||||||
|
#### DEFAULT_MISSING_DATA
|
||||||
|
|
||||||
|
The value that should be used for `request.data` when the client did not send any data in the request body. This
|
||||||
|
setting applies only if the client did not send a header indicating form encoding.
|
||||||
|
|
||||||
|
Default: `None`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Generic view settings
|
## Generic view settings
|
||||||
|
|
|
@ -341,7 +341,7 @@ class Request:
|
||||||
if media_type and is_form_media_type(media_type):
|
if media_type and is_form_media_type(media_type):
|
||||||
empty_data = QueryDict('', encoding=self._request._encoding)
|
empty_data = QueryDict('', encoding=self._request._encoding)
|
||||||
else:
|
else:
|
||||||
empty_data = {}
|
empty_data = api_settings.DEFAULT_MISSING_DATA
|
||||||
empty_files = MultiValueDict()
|
empty_files = MultiValueDict()
|
||||||
return (empty_data, empty_files)
|
return (empty_data, empty_files)
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ DEFAULTS = {
|
||||||
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
|
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
|
||||||
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
|
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
|
||||||
'DEFAULT_VERSIONING_CLASS': None,
|
'DEFAULT_VERSIONING_CLASS': None,
|
||||||
|
'DEFAULT_MISSING_DATA': None,
|
||||||
|
|
||||||
# Generic view behavior
|
# Generic view behavior
|
||||||
'DEFAULT_PAGINATION_CLASS': None,
|
'DEFAULT_PAGINATION_CLASS': None,
|
||||||
|
|
|
@ -56,14 +56,14 @@ class TestContentParsing(TestCase):
|
||||||
Ensure request.data returns empty QueryDict for GET request.
|
Ensure request.data returns empty QueryDict for GET request.
|
||||||
"""
|
"""
|
||||||
request = Request(factory.get('/'))
|
request = Request(factory.get('/'))
|
||||||
assert request.data == {}
|
assert request.data is None
|
||||||
|
|
||||||
def test_standard_behaviour_determines_no_content_HEAD(self):
|
def test_standard_behaviour_determines_no_content_HEAD(self):
|
||||||
"""
|
"""
|
||||||
Ensure request.data returns empty QueryDict for HEAD request.
|
Ensure request.data returns empty QueryDict for HEAD request.
|
||||||
"""
|
"""
|
||||||
request = Request(factory.head('/'))
|
request = Request(factory.head('/'))
|
||||||
assert request.data == {}
|
assert request.data is None
|
||||||
|
|
||||||
def test_request_DATA_with_form_content(self):
|
def test_request_DATA_with_form_content(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -42,6 +42,7 @@ class BasicSerializer(serializers.Serializer):
|
||||||
@api_view(['POST'])
|
@api_view(['POST'])
|
||||||
def post_view(request):
|
def post_view(request):
|
||||||
serializer = BasicSerializer(data=request.data)
|
serializer = BasicSerializer(data=request.data)
|
||||||
|
serializer.allow_null = ('allow_null' in request.query_params)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
return Response(serializer.validated_data)
|
return Response(serializer.validated_data)
|
||||||
|
|
||||||
|
@ -191,7 +192,26 @@ class TestAPITestClient(TestCase):
|
||||||
path='/view/', data={'valid': 123, 'invalid': {'a': 123}}
|
path='/view/', data={'valid': 123, 'invalid': {'a': 123}}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_empty_post_uses_default_boolean_value(self):
|
def test_missing_post_payload_causes_400(self):
|
||||||
|
response = self.client.post(
|
||||||
|
'/post-view/',
|
||||||
|
data=None,
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert response.data['non_field_errors'] == ['No data provided']
|
||||||
|
|
||||||
|
def test_missing_post_payload_allow_null_causes_200(self):
|
||||||
|
response = self.client.post(
|
||||||
|
'/post-view/?allow_null=1',
|
||||||
|
data=None,
|
||||||
|
content_type='application/json'
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data is None
|
||||||
|
|
||||||
|
@override_settings(REST_FRAMEWORK={'DEFAULT_MISSING_DATA': {}})
|
||||||
|
def test_missing_post_payload_coerced_dict_uses_default_boolean_value(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/post-view/',
|
'/post-view/',
|
||||||
data=None,
|
data=None,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user