序列化可选的嵌套结构:QueryDict和普通字典之间的区别?

时间:2015-08-01 06:45:33

标签: python django django-rest-framework

在使用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和普通字典之间的区别是什么?或者我是以错误的方式接近这个?

2 个答案:

答案 0 :(得分:5)

DRF定义了多个解析器类,用于解析具有不同媒体类型的请求内容。

request.data通常是QueryDict或普通字典,具体取决于用于解析请求内容的解析器。

  • JSONParser:

它解析JSON请求内容,即.media_typeapplication/json的内容。

  • FormParser

它解析HTML表单内容。在此处, request.data填充了QueryDict个数据。这些数据的.media_typeapplication/x-www-form-urlencoded

  • MultiPartParser

它解析支持文件上传的多部分HTML表单内容。同样, request.data都填充了QueryDict 。这些都有 .media_typemultipart/form-data

  • FileUploadParser

它解析原始文件上传内容。 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指出我正确的方向!