序列化可选的嵌套结构:QueryDict和normal dict之间的区别? [英] Serializing optionally nested structures: Difference between QueryDict and normal dict?

查看:267
本文介绍了序列化可选的嵌套结构:QueryDict和normal dict之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用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 =

class Meta:
fields =('a','child')

def perform_create(self,serializer):
#TODO:创建嵌套对象。
pass

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



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

 code> 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 '],'field_c':[你需要这个字段']}}

在实际的网站上,API得到一个正常的dict,因此工作正常。在测试过程中,使用像 client.post('url',data = dict(field_a = 3))这样的东西会导致一个QueryDict被传递给视图,因此不工作。



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

解决方案

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



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




  • JSONParser:



它解析JSON请求内容,即以 .media_type application / json




  • FormParser



它解析HTML表单内容。这里, request.data 填充了一个 QueryDict 的数据。这些有code> .media_type as application / x-www-form-urlencoded




  • MultiPartParser



它解析多部分HTML表单内容,支持文件上传。此外, request.data 都填充了一个 QueryDict 。这些
.media_type as multipart / form-data




  • FileUploadParser



它解析原始文件上传内容。 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.

  • JSONParser:

It parses the JSON request content i.e. content with .media_type as application/json.

  • FormParser

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.

  • MultiPartParser

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.

  • FileUploadParser

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')

这篇关于序列化可选的嵌套结构:QueryDict和normal dict之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆