Django Views: Когда request.data является диктом против QueryDict?

У меня возникли проблемы с тем, что 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.data это dict, но в производстве представление вылетает, потому что 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?

1 ответ

Решение

Ваш тест не является отражением вашего фактического вызова.

В своем тесте вы публикуете JSON, который затем доступен в виде комментария от request.data, Но ваш вызов curl публикует стандартные данные формы, которые доступны как QueryDict. Это поведение управляется parsers атрибут вашего представления или настройки DEFAULT_PARSER_CLASSES - и далее отметим, что это функциональность, специально предоставляемая django-rest-framework, которую вы должны были пометить в своем вопросе, а не сам Django.

На самом деле вы должны проверить то же самое, что и вы; отправьте JSON из curl или отправьте тест для публикации данных формы.

Когда ваш тип содержимого 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 имеет метод получения списков. но это не может принести продиктованное значение.
преобразовать имя str в массив

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

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

И определить пользовательскую форму 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

И перезаписать DEFAULT_PARSER_CLASSES.
https://www.django-rest-framework.org/api-guide/settings/

Другие вопросы по тегам