简体   繁体   English

Django 视图:request.data 什么时候是 dict 还是 QueryDict?

[英]Django Views: When is request.data a dict vs a QueryDict?

I have run into some trouble with the issue, that request.data sometimes is a dict (especially when testing) and sometimes a QueryDict instance (when using curl).我在这个问题上遇到了一些麻烦,request.data 有时是一个dict (尤其是在测试时),有时是一个QueryDict实例(当使用 curl 时)。

This is especially a problem because apparently there is a big difference when calling a view using curl like so:这尤其是一个问题,因为在使用 curl 调用视图时显然存在很大差异,如下所示:

curl -X POST --data "some_float=1.23456789012123123" "http://localhost:8000/myview"

Or using the django_webtest client like so:或者像这样使用 django_webtest 客户端:

class APIViewTest(WebTest):
    def test_testsomething(self):
        self.app.post(url=url, params=json.dumps({some_float=1.26356756467}))

And then casting that QueryDict to a dict like so然后将该 QueryDict 转换为这样的字典

new_dict = dict(**request.data)
my_float = float(new_dict['some_float'])

Everything works fine in the tests, as there request.data is a dict , but in production the view crashes because new_dict['some_float'] is actually a list with one element, and not as expected a float.在测试中一切正常,因为request.data是一个dict ,但在生产中视图崩溃,因为new_dict['some_float']实际上是一个包含一个元素的列表,而不是预期的浮点数。

I have considered fixing the issue like so:我已经考虑过像这样解决这个问题:

    if type(request.data) is dict:
        new_dict = dict(**request.data)
    else:
        new_dict = dict(**request.data.dict())

which feels very wrong as the tests would only test line 2, and (some? all?) production code would run line 4.这感觉非常错误,因为测试只会测试第 2 行,并且(一些?全部?)生产代码将运行第 4 行。

So while I am wondering why QueryDict behaves in this way, I would rather know why and when response.data is a QueryDict in the first place.因此,当我想知道为什么 QueryDict 会以这种方式运行时,我宁愿知道为什么以及何时 response.data 是一个QueryDict放在首位。 And how I can use django tests to simulate this behavior.以及我如何使用 django 测试来模拟这种行为。 Having different conditions for production and testing systems is always troublesome and sometimes unavoidable, but in this case I feel like it could be fixed.生产和测试系统有不同的条件总是很麻烦,有时是不可避免的,但在这种情况下,我觉得它可以解决。 Or is this a specific issue related to django_webtest?或者这是与 django_webtest 相关的特定问题?

Your test isn't a reflection of your actual curl call.您的测试并不反映您的实际 curl 调用。

In your test, you post JSON, which is then available as a dict from request.data .在您的测试中,您发布了 JSON,然后它可以作为request.data的 dict 使用。 But your curl call posts standard form data, which is available as a QueryDict.但是您的 curl 调用会发布标准表单数据,该数据可用作 QueryDict。 This behaviour is managed by the parsers attribute of your view or the DEFAULT_PARSER_CLASSES settings - and further note that this is functionality specifically provided by django-rest-framework, not Django itself.此行为由视图的parsers属性或 DEFAULT_PARSER_CLASSES 设置管理 - 并进一步注意,这是 django-rest-framework 专门提供的功能,而不是 Django 本身。

Really you should test the same thing as you are doing;实际上,您应该测试与您正在做的相同的事情; either send JSON from curl or get your test to post form-data.从 curl 发送 JSON 或让您的测试发布表单数据。

When your request content_type is "application/x-www-form-urlencoded", request.Data become QueryDict.当您的请求 content_type 为“application/x-www-form-urlencoded”时,request.Data 变为 QueryDict。

see FormParser class.请参阅 FormParser 类。
https://github.com/encode/django-rest-framework/blob/master/rest_framework/parsers.py https://github.com/encode/django-rest-framework/blob/master/rest_framework/parsers.py

And

QueryDict has get lists method. QueryDict 有获取列表方法。 but it can't fetch dict value.但它无法获取 dict 值。
convert name str to array.将名称 str 转换为数组。

<input name="items[name]" value="Example">
<input name="items[count]" value="5">  

https://pypi.org/project/html-json-forms/ https://pypi.org/project/html-json-forms/

And define custom form paser.并定义自定义表单paser。

class CustomFormParser(FormParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded'

def parse(self, stream, media_type=None, parser_context=None):
    """
    Parses the incoming bytestream as a URL encoded form,
    and returns the resulting QueryDict.
    """
    parser_context = parser_context or {}
    encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
    data = QueryDict(stream.read(), encoding=encoding)
    return parse_json_form(data.dict()) # return dict

And overwite DEFAULT_PARSER_CLASSES.并覆盖 DEFAULT_PARSER_CLASSES。
https://www.django-rest-framework.org/api-guide/settings/#default_parser_classes https://www.django-rest-framework.org/api-guide/settings/#default_parser_classes

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM