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

Nus*_*chk 1 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
Run Code Online (Sandbox Code Playgroud)

(我省略了perform_create中的代码,因为它与问题无关).

现在,传递一个普通的dict作为数据参数工作正常:

ser = ParentSerializer(data=dict(field_a=3))
ser.is_valid(raise_exception=True)
Run Code Online (Sandbox Code Playgroud)

但是传递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.']}}
Run Code Online (Sandbox Code Playgroud)

在实际的网站上,API得到一个正常的字典,因此工作正常.但是在测试期间,使用类似的东西client.post('url', data=dict(field_a=3))会导致QueryDict传递给视图,因此无法工作.

所以我的问题是:QueryDict和普通字典有什么区别?或者我是以错误的方式接近这个?

Rah*_*pta 5

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

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

  • JSONParser:

它解析JSON请求内容,即内容为.media_typeas application/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如何确定解析器?

当DRF访问时request.data,它会检查Content-Type传入请求上的标头,然后确定使用哪个解析器来解析请求内容.

您需要Content-Type在发送数据时设置标头,否则它将使用multipart或form解析器来解析请求内容并为您提供QueryDictin request.data而不是字典.

根据DRF文档,

如果您未设置内容类型,则大多数客户端将默认使用 'application/x-www-form-urlencoded',这可能不是您想要的.

因此,当发送json编码数据时,还要将Content-Type标头设置为application/json,然后它将按预期工作.

为什么request.data有时候QueryDict,有时候dict

这样做是因为不同的编码具有不同的数据结构和属性.

例如,表单数据是支持相同值的多个键的编码,而json不支持.

同样在JSON数据的情况下,request.DATA可能根本不是dict,它可以是列表或任何其他json原语.

查看此Google网上论坛帖子,了解相同内容.

你需要做什么?

您可以format='json'在测试时添加POSTing将设置内容类型的数据以及正确序列化数据.

client.post('url', format='json', data=dict(field_a=3))
Run Code Online (Sandbox Code Playgroud)

您还可以使用content-type参数发送JSON编码的内容.

client.post('url', json.dumps(dict(field_a=3)), content_type='application/json')
Run Code Online (Sandbox Code Playgroud)