更新时间:2023-11-22 21:31:28
DRF定义了多个解析器类,用于解析具有不同媒体的请求内容类型。
request.data
通常是一个 QueryDict
或正常字典取决于用于解析请求内容的解析器。
它解析JSON请求内容,即以 .media_type
为 application / json
。
它解析HTML表单内容。这里, request.data
填充了一个 QueryDict
的数据。这些有code> .media_type as application / x-www-form-urlencoded
。
它解析多部分HTML表单内容,支持文件上传。此外, request.data
都填充了一个 QueryDict
。这些 .media_type
as multipart / form-data
。
它解析原始文件上传内容。 request.data
属性是一个包含上传文件的单键文件
的字典 。
DRF 确定解析器?
当DRF访问 request.data
,它检查传入请求中的 Content-Type
头,然后确定用于解析请求内容的解析器。 / p>
发送数据时,您需要设置 Content-Type
标题,否则将使用多部分或形式解析器来解析请求内容,并在 request.data
中为您提供 QueryDict
而不是字典。
根据DRF文档,
如果没有设置内容类型,大多数客户端将默认使用
'application / x-www-form-urlenc oded'
,可能不是你想要的。
所以当发送json编码数据时,也设置 Content-Type
头到 application / json
,然后它将按预期工作。
为什么request.data有时是 QueryDict
,有时 dict
?
这是因为不同的编码具有不同的数据结构和属性。
例如,表单数据是一个支持多个相同值的键的编码,而json不支持。
另外在这种情况下的JSON数据, request.DATA
可能不是一个 dict
,它可能是一个列表或任何其他json原语。
查看这个 Google Groups线程大致相同。
您需要做什么?
当 POSTing
时,您可以在测试中添加 format ='json'
数据将设置内容类型以及序列化数据正确。
client.post ('url',format ='json',data = dict(field_a = 3))
你也可以使用 content-type
参数发送JSON编码的内容。
client.post('url',json.dumps(dict(field_a = 3)),content_type ='application / json')
I'm running into weird behavior when writing nested structures with django-rest and then trying to test them using django-rest's test client. The nested child object should be optional.
Here's a sample serializer:
from rest_framework import serializers
class OptionalChildSerializer(serializers.Serializer):
field_b = serializers.IntegerField()
field_c = serializers.IntegerField()
class Meta:
fields = ('field_b', 'field_c', )
class ParentSerializer(serializers.Serializer):
field_a = serializers.IntegerField()
child = OptionalChildSerializer(required=False, many=False)
class Meta:
fields = ('a', 'child',)
def perform_create(self, serializer):
# TODO: create nested object.
pass
(I've omitted the code in perform_create, as it's not relevant to the question).
Now, passing a normal dict as data argument works just fine:
ser = ParentSerializer(data=dict(field_a=3))
ser.is_valid(raise_exception=True)
But passing a QueryDict instead will fail:
from django.http import QueryDict
ser = ParentSerializer(data=QueryDict("field_a=3"))
ser.is_valid(raise_exception=True)
ValidationError: {'child': {'field_b': [u'This field is required.'], 'field_c': [u'This field is required.']}}
On the actual web site, the API gets a normal dict and thus works fine. During testing however, using something like client.post('url', data=dict(field_a=3))
will result in a QueryDict being passed to the view, and hence not work.
So my question is: what's the difference between the QueryDict and normal dict? Or am I approaching this the wrong way?
DRF defines multiple parser classes for parsing the request content having different media types.
request.data
will normally be a QueryDict
or a normal dictionary depending on the parser used to parse the request content.
It parses the JSON request content i.e. content with .media_type
as application/json
.
It parses the HTML form content. Here, request.data
is populated with a QueryDict
of data. These have .media_type
as application/x-www-form-urlencoded
.
It parses multipart HTML form content, which supports file uploads. Here also, both request.data
is populated with a QueryDict
. These have
.media_type
as multipart/form-data
.
It parses raw file upload content. The request.data
property is a dictionary with a single key file
containing the uploaded file.
How does DRF determines the parser?
When DRF accesses the request.data
, it examines the Content-Type
header on the incoming request and then determines which parser to use to parse the request content.
You will need to set the Content-Type
header when sending the data otherwise it will use either a multipart or a form parser to parse the request content and give you a QueryDict
in request.data
instead of a dictionary.
As per DRF docs,
If you don't set the content type, most clients will default to using
'application/x-www-form-urlencoded'
, which may not be what you wanted.
So when sending json encoded data, also set the Content-Type
header to application/json
and then it will work as expected.
Why request.data is sometimes QueryDict
and sometimes dict
?
This is done because different encodings have different datastructures and properties.
For example, form data is an encoding that supports multiple keys of the same value, whereas json does not support that.
Also in case of JSON data, request.DATA
might not be a dict
at all, it could be a list or any of the other json primitives.
Check out this Google Groups thread about the same.
What you need to do?
You can add format='json'
in the tests when POSTing
the data which will set the content-type as well as serialize the data correctly.
client.post('url', format='json', data=dict(field_a=3))
You can also send JSON-encoded content with content-type
argument.
client.post('url', json.dumps(dict(field_a=3)), content_type='application/json')