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
},
]
}