Возможный дубликат --- Django REST: загрузка и сериализация нескольких изображений .
Из документированного вложенного сериализатора DRF ,
По умолчанию вложенные сериализаторы доступны только для чтения. Если вы хотите поддерживать операции записи во вложенное поле сериализатора, вам нужно создать create()
и / или update()
методы, чтобы явно указывало, как дочерние отношения должны быть сохранены.
Из этого ясно, что дочерний сериализатор (BinaryFileSerializer
) не будет вызывать свой собственный метод create()
, если явно не вызывается.
Цель вашего запроса HTTP POST
- создать новый экземпляр Submission
(и экземпляр BinaryFile
) , Процесс создания проходит по методу create()
сериализатора SubmissionCreateSerializer
, который вы должны переопределить. Таким образом, он будет действовать / выполняться в соответствии с вашим кодом.
UPDATE-1
Что нужно запомнить
1. AFAIK, мы не можем отправить вложенный multipart/form-data
2. Здесь я только пытаюсь реализовать сценарий наименьшего случая
3. Я протестировал это решение с помощью POSTMAN инструмента тестирования остальных API.
4. Этот метод может быть сложным (пока мы не нашли лучший).
5. Предполагая, что ваш класс представления является подклассом ModelViewSet
class
Что я собираюсь делать?
1. Поскольку мы не можем отправлять файлы / данные вложенным способом, мы должны отправить их в плоском режиме.
изображение-1
2. Переопределите метод __init__()
сериализатора SubmissionSerializer
и динамически добавьте столько же атрибутов FileField()
в соответствии с request.FILES
data.
Мы могли бы как-то использовать ListSerializer
или ListField
здесь. К сожалению, я не мог найти способ: (
# init method of "SubmissionSerializer"
def __init__(self, *args, **kwargs):
file_fields = kwargs.pop('file_fields', None)
super().__init__(*args, **kwargs)
if file_fields:
field_update_dict = {field: serializers.FileField(required=False, write_only=True) for field in file_fields}
self.fields.update(**field_update_dict)
Итак, что ID file_fields
здесь?
Так как данные формы является парой значение ключа, каждый файл данных должен быть связан с ключом. Здесь, в image-1 , вы можете увидеть file_1
и file_2
.
3. Теперь нам нужно передать значения file_fields
из view
. Поскольку эта операция создает новый экземпляр, нам необходимо переопределить метод create()
класса *1136* API .
<b># complete view code</b>
from rest_framework import status
from rest_framework import viewsets
class SubmissionAPI(viewsets.ModelViewSet):
queryset = Submission.objects.all()
serializer_class = SubmissionSerializer
def create(self, request, *args, **kwargs):
<b># main thing starts
file_fields = list(request.FILES.keys()) # list to be passed to the serializer
serializer = self.get_serializer(data=request.data, file_fields=file_fields)
# main thing ends</b>
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
4. Теперь все значения будут правильно сериализованы. Пришло время переопределить create()
метод SubmissionSerializer()
, чтобы отобразить отношения
def create(self, validated_data):
from django.core.files.uploadedfile import InMemoryUploadedFile
validated_data_copy = validated_data.copy()
validated_files = []
for key, value in validated_data_copy.items():
if isinstance(value, InMemoryUploadedFile):
validated_files.append(value)
validated_data.pop(key)
submission_instance = super().create(validated_data)
for file in validated_files:
BinaryFile.objects.create(submission=submission_instance, file=file)
return submission_instance
5. Вот и все !!!
Полный фрагмент кода
<b># serializers.py</b>
from rest_framework import serializers
<b>from django.core.files.uploadedfile import InMemoryUploadedFile</b>
class SubmissionSerializer(serializers.ModelSerializer):
<b>def __init__(self, *args, **kwargs):
file_fields = kwargs.pop('file_fields', None)
super().__init__(*args, **kwargs)
if file_fields:
field_update_dict = {field: serializers.FileField(required=False, write_only=True) for field in file_fields}
self.fields.update(**field_update_dict)
def create(self, validated_data):
validated_data_copy = validated_data.copy()
validated_files = []
for key, value in validated_data_copy.items():
if isinstance(value, InMemoryUploadedFile):
validated_files.append(value)
validated_data.pop(key)
submission_instance = super().create(validated_data)
for file in validated_files:
BinaryFile.objects.create(submission=submission_instance, file=file)
return submission_instance</b>
class Meta:
model = Submission
fields = '__all__'
<b># views.py
from rest_framework import status</b>
from rest_framework import viewsets
class SubmissionAPI(viewsets.ModelViewSet):
queryset = Submission.objects.all()
serializer_class = SubmissionSerializer
<b>def create(self, request, *args, **kwargs):
# main thing starts
file_fields = list(request.FILES.keys()) # list to be passed to the serializer
serializer = self.get_serializer(data=request.data, file_fields=file_fields)
# main thing ends
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)</b>
Скриншоты и прочее
1. ПОСТМАН консоль
2. Джанго Шелл
In [2]: Submission.objects.all()
Out[2]: <QuerySet [<Submission: Submission object>]>
In [3]: sub_obj = Submission.objects.all()[0]
In [4]: sub_obj
Out[4]: <Submission: Submission object>
In [5]: sub_obj.__dict__
Out[5]:
{'_state': <django.db.models.base.ModelState at 0x7f529a7ea240>,
'id': 5,
'issued_at': datetime.datetime(2019, 3, 27, 8, 45, 42, 193943, tzinfo=<UTC>),
'completed': False,
'atomic_id': 1}
In [6]: sub_obj.binary_files.all()
Out[6]: <QuerySet [<BinaryFile: uploads/binary/logo-800.png>, <BinaryFile: uploads/binary/Doc.pdf>, <BinaryFile: uploads/binary/invoice_2018_11_29_04_57_53.pdf>, <BinaryFile: uploads/binary/Screenshot_from_2019-02-13_16-22-53.png>]>
In [7]: for _ in sub_obj.binary_files.all():
...: print(_)
...:
uploads/binary/logo-800.png
uploads/binary/Doc.pdf
uploads/binary/invoice_2018_11_29_04_57_53.pdf
uploads/binary/Screenshot_from_2019-02-13_16-22-53.png
3. Скриншот администратора Django