Django serializer тестовый пост файла с информацией о пользователе

Я пытаюсь протестировать загрузку файла следующим образом:

def test_this(self, api_client, login_as):
    user = login_as('quality-controller')
    url = reverse('icsr-list')
    organization = Organization(name="test")
    organization.save()


    data = {
        "organization": organization.id,
        "import_file": FileGenerator.generate_text_file('txt'),
        "user": {
            "id": user.id,
            "username": user.username,
        }
    }

    response = api_client.post(url, data, format='json')

Но я получаю следующее сообщение об ошибке:

b'{"import_file": ["Представленные данные не являются файлом. Проверьте тип кодировки в форме."]}'

.

Я также пытался использовать: format='multipart', но тогда я получаю следующую ошибку:

AssertionError: Тестовые данные содержали словарное значение для ключа 'user', но многочастная загрузка не поддерживает вложенные данные. Возможно, вам стоит рассмотреть возможность использования format='json' в этом тестовом примере.

Как я могу решить эту проблему?

Вот как я решаю эту проблему:

Простейшее: сплющить форму

Устраните эту проблему, заставив ваш сериализатор использовать user_id и user_username и исправьте ее на стороне сервера в методе validate(self, attrs) сериализатора.

def validate(self, attrs):
    attrs["user"] = {
        "id": attrs.pop("user_id"), 
        "name": attrs.pop("user_username.")
    }
    return attrs

Лучше всего, если вас не смущает размер: B64 Fields

Вы можете закодировать поле file в base64 и передать его в json. Затем, чтобы декодировать его на стороне сервера, вы напишете (или найдете) простой Base64FileField() для DRF.

class UploadedBase64ImageSerializer(serializers.Serializer):
    file = Base64ImageField(required=False)
    created = serializers.DateTimeField()

Альтернатива - Сплющивание данных формы

Вы не можете передавать вложенные данные, но вы можете сплющить вложенные dict и передать их службе DRF. Сериализаторы действительно могут понимать вложенные данные, если имена полей корректны.

Я не знаю, стандартизирован ли этот формат имени поля, но это то, что сработало для меня после экспериментов. Я использую его только для сервисного взаимодействия с drf, поэтому вам придется клонировать его в JS, но вы можете использовать python в модульных тестах. Дайте мне знать, если это работает для вас.

def flatten_dict_for_formdata(input_dict, array_separator="[{i}]"):
    """
    Recursively flattens nested dict()s into a single level suitable
    for passing to a library that makes multipart/form-data posts.
    """

    def __flatten(value, prefix, result_dict, previous=None):
        if isinstance(value, dict):
            # If we just processed a dict, then separate with a "."
            # Don't do this if it is an object inside an array.  
            # In that case the [:id] _is_ the separator, adding 
            # a "." like list[1].name will break but list[x]name 
            # is correct (at least for DRF/django decoding)
            if previous == "dict":
                prefix += "."

            for key, v in value.items():
                __flatten(
                    value=v,
                    prefix=prefix + key,
                    result_dict=result_dict,
                    previous="dict"
                )
        elif isinstance(value, list) or isinstance(value, tuple):
            for i, v in enumerate(value):
                __flatten(
                    value=v,
                    prefix=prefix + array_separator.format(i=i),  # e.g. name[1]
                    result_dict=result_dict,
                    previous="array"
                )
        else:
            result_dict[prefix] = value

        # return her to simplify the caller's life.  ignored during recursion
        return result_dict

    return __flatten(input_dict, '', OrderedDict(), None)
# flatten_dict_for_formdata({...}):
{                                   # output field name
    "file": SimpleUploadFile(...),  # file
    "user": {                       
        "id": 1,                    # user.id
        "name": "foghorn",          # user.name        
        "jobs": [                           
            "driver",               # user.jobs[0]
            "captain",              # user.jobs[1]
            "pilot"                 # user.jobs[1]
        ]
    },
    "objects": [
        {
            "type": "shoe",         # objects[0]type
            "size": "44"            # objects[0]size
        },
    ]
}
Вернуться на верх