在使用django-rest编写嵌套结构然后尝试使用django-rest的测试客户端测试它们时,我遇到了奇怪的行为。嵌套的子对象应该是可选的。
这是一个示例序列化器:
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
(我已经省略了perform_create中的代码,因为它与问题无关)。
现在,传递一个普通的dict作为数据参数可以正常工作:
ser = ParentSerializer(data=dict(field_a=3))
ser.is_valid(raise_exception=True)
但是传递QueryDict会失败:
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.']}}
在实际网站上,API获得正常的字典,因此工作正常。但是,在测试期间,使用类似client.post('url', data=dict(field_a=3))
的内容会导致QueryDict传递给视图,因此无效。
所以我的问题是:QueryDict和普通字典之间的区别是什么?或者我是以错误的方式接近这个?
答案 0 :(得分:5)
DRF定义了多个解析器类,用于解析具有不同媒体类型的请求内容。
request.data
通常是QueryDict
或普通字典,具体取决于用于解析请求内容的解析器。
它解析JSON请求内容,即.media_type
为application/json
的内容。
它解析HTML表单内容。在此处, request.data
填充了QueryDict
个数据。这些数据的.media_type
为application/x-www-form-urlencoded
。
它解析支持文件上传的多部分HTML表单内容。同样, request.data
都填充了QueryDict
。这些都有
.media_type
为multipart/form-data
。
它解析原始文件上传内容。 request.data
属性是一个字典,其中包含一个包含上传文件的密钥file
。
DRF determines如何解析?
当DRF访问request.data
时,它会检查传入请求的Content-Type
标头,然后确定使用哪个解析器来解析请求内容。
您需要在发送数据时设置Content-Type
标头,否则它将使用多部分或表单解析器来解析请求内容,并在QueryDict
中为您提供request.data
而不是字典。
根据DRF文档,
如果您未设置内容类型,则大多数客户端将默认使用
'application/x-www-form-urlencoded'
,这可能不是您想要的。
因此,在发送json编码数据时,还要将Content-Type
标头设置为application/json
,然后它将按预期工作。
为什么request.data有时为QueryDict
,有时为dict
?
这样做是因为不同的编码具有不同的数据结构和属性。
例如,表单数据是一种支持相同值的多个键的编码,而json不支持。
同样在JSON数据的情况下,request.DATA
可能根本不是dict
,它可以是列表或任何其他json原语。
请查看此Google Groups thread相同内容。
您需要做什么?
当format='json'
设置内容类型的数据以及正确序列化数据时,您可以在测试中添加POSTing
。
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')
答案 1 :(得分:0)
django-rest测试客户端不会自动将数据序列化为json,但使用multipart / form,这会产生QueryDict。
然而,有一个格式选项,描述为in the docs。以下测试代码可以正常工作:
client.post('url', format='json', data=dict(field_a=3))
我仍然对普通字典和QueryDict之间的不同序列化程序行为感到困惑,但是......
感谢Rajesh指出我正确的方向!