【问题标题】:Django Views: When is request.data a dict vs a QueryDict?Django 视图:request.data 何时是 dict 与 QueryDict?
【发布时间】:2018-11-08 10:55:40
【问题描述】:

我遇到了一些问题,request.data 有时是 dict(尤其是在测试时),有时是 QueryDict 实例(在使用 curl 时)。

这尤其是一个问题,因为使用 curl 调用视图时显然存在很大差异,如下所示:

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

或者像这样使用 django_webtest 客户端:

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

然后将 QueryDict 转换为这样的字典

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

在测试中一切正常,因为 request.datadict,但在生产中视图崩溃,因为 new_dict['some_float'] 实际上是一个包含一个元素的列表,而不是预期的浮动。

我考虑过这样解决问题:

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

这感觉很不对劲,因为测试只会测试第 2 行,而(一些?全部?)生产代码会运行第 4 行。

因此,虽然我想知道为什么 QueryDict 会以这种方式运行,但我宁愿首先知道 response.data 为什么以及何时是 QueryDict。以及我如何使用 django 测试来模拟这种行为。生产和测试系统的条件不同总是很麻烦,有时是不可避免的,但在这种情况下,我觉得它可以解决。还是这是与 django_webtest 相关的特定问题?

【问题讨论】:

  • 因为您可以为同一个键传递两个值,例如:curl -X POST --data "some_float=1.23456789012123123" --data "some_float=3.14" "http://localhost:8000/myview"。这就是他们最初构建QueryDict 的本质原因。
  • 但是 request.data 不应该总是一个 QueryDict 实例吗?
  • 它是,只是因为你在这里将它转换为adict,它当然是一个字典。
  • 不,不是。 type(request.data) is dict 在本地测试时返回 True,如我的问题中所述。

标签: python django django-rest-framework django-webtest


【解决方案1】:

您的测试并不反映您的实际 curl 调用。

在您的测试中,您发布 JSON,然后可作为来自 request.data 的字典使用。但是您的 curl 调用会发布标准表单数据,这些数据可作为 QueryDict 使用。此行为由视图的 parsers 属性或 DEFAULT_PARSER_CLASSES 设置管理 - 并进一步注意,这是 django-rest-framework 专门提供的功能,而不是 Django 本身。

确实,您应该测试与您正在做的事情相同的事情;从 curl 发送 JSON 或让您的测试发布表单数据。

【讨论】:

  • 不幸的是,我的客户都发送了。我希望 django 有某种界面可以以相同的方式处理两者
  • 但正如我刚才所说,这是由解析器设置控制的 DRF 功能,您可以随意覆盖它或设置自己的。无论如何,“request.data 何时是 dict 与 querydict”的问题是由“何时发送 JSON 与 form-data”来回答的。
  • 另外,request.data 以相同方式处理两者的接口。我不知道你为什么认为你需要转换为字典,QueryDict 在所有方面都与字典完全一样,除了它可以额外处理同一个键的多个值。如果您没有多个值,只需像 dict 一样使用它,您不会注意到任何差异。
  • 它的行为方式并不完全相同。一个是可变的,另一个不是。
  • 我希望这个答案更明确地说明如何解决问题,而不是仅仅指出来
【解决方案2】:

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

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

QueryDict 有获取列表的方法。但它无法获取 dict 值。
将名称 str 转换为数组。

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

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

并定义自定义表单解析器。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 2022-11-12
    • 2015-04-17
    • 2018-08-02
    • 2016-08-05
    • 2023-03-03
    • 1970-01-01
    相关资源
    最近更新 更多