#python #django #database #django-rest-framework
#python #django #База данных #django-rest-framework
Вопрос:
Я пытаюсь загрузить файл csv, а затем использовать его для заполнения таблицы в базе данных (создание нескольких объектов).
serializers.py:
def instantiate_batch_objects(data_list, user):
return [
WorkData(
work=db_obj['work'],
recordTime=db_obj['recordTime'],
user=user
) for db_obj in data_list
]
class FileUploadSerializer(serializers.ModelSerializer):
filedata = serializers.FileField(write_only=True)
class Meta:
model = WorkData
fields = ['user', 'filedata']
def create(self, validated_data):
file = validated_data.pop('filedata')
data_list = csv_file_parser(file)
batch = instantiate_batch_objects(data_list, validated_data['user'])
work_data_objects = WorkData.objects.bulk_create(batch)
# print(work_data_objects[0])
return work_data_objects
views.py:
class FileUploadView(generics.CreateAPIView):
queryset = WorkData.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = FileUploadSerializer
# I guess, this is not need for my case.
def get_serializer(self, *args, **kwargs):
print(kwargs.get('data'))
if isinstance(kwargs.get('data', {}), list):
kwargs['many'] = True
return super().get_serializer(*args, **kwargs)
models.py
class WorkData(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='work_data',
)
work = models.IntegerField(blank=False, null=False)
recordTime = models.DateTimeField(blank=False, null=True)
Когда я загружаю файл и публикую его, я получаю эту ошибку:
Получил ошибку AttributeError при попытке получить значение для поля
user
в сериализатореFileUploadSerializer
. Поле сериализатора может быть названо неправильно и не соответствовать какому-либо атрибуту или ключуlist
экземпляра. Исходный текст исключения был: объект ‘list’ не имеет атрибута ‘user’.
Но я вижу, что таблица успешно заполнена в базе данных. Что я должен вернуть из create
метода FileUploadSerializer
?
Комментарии:
1.
FileUploadSerializer.create()
предполагается повторный запуск только одного экземпляра модели.2. @ArakkalAbu Мне нужно создавать объекты массово.
3. @ArakkalAbu, кстати, вы можете вернуть несколько
4. можете ли вы добавить фрагмент к данным, которые вы передаете своей конечной точке?
5. Я передаю файл в конечную точку api. Затем считывание данных из файла и создание объектов, переопределяющих метод создания класса сериализатора.
Ответ №1:
Хорошо, попробовав пример самостоятельно, я смог воспроизвести ошибки, я лучше понимаю, почему это происходит сейчас.
Во-первых, давайте поместим реализацию create() в класс view здесь
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
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)
Исходная ошибка Got AttributeError when attempting to get a value for field user
… etc произошло из create()
-за того, что in FileUploadView
возвращает serializer.data
ожидающие поля user
, а filedata
but create()
on FileUploadSerializer
возвращает список объектов, так что теперь вы можете понять, почему это происходит.
Вы можете решить эту проблему, переопределив create()
on FileUploadView
и сериализовав возвращенные данные сериализатора с помощью a WorkDataSerializer
, которые вы создадите
Например:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
workData = WorkDataSerializer(data=serializer.data)
return Response(workData.data, status=status.HTTP_201_CREATED, headers=headers)
ИЛИ вы можете сделать это на уровне сериализатора — что я предпочитаю —
Например:
class FileUploadSerializer(serializers.ModelSerializer):
filedata = serializers.FileField(write_only=True)
created_objects_from_file = serializers.SerializerMethodField()
def get_created_objects_from_file(self, obj):
file = self.validated_data.pop('filedata')
data_list = csv_file_parser(file)
batch = instantiate_batch_objects(data_list, self.validated_data['user'])
work_data_objects = WorkData.objects.bulk_create(batch)
return WorkDataSerializer(work_data_objects, many = True).data
class Meta:
model = WorkData
fields = ['user', 'filedata']
class WorkDataSerializer(serializers.Serializer):
# fields of WorkData model you want to return
Это должно работать без проблем, обратите внимание, что SerializerMethodField
по умолчанию используется только для чтения
смотрите https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
Комментарии:
1. Мой голос за поиск проблемы. Я тоже понял это, и мне пришло в голову наивное решение — просто вернуть один объект из списка :
return work_data_objects[0]
.2. Я предположил, что возврат всех созданных объектов является обязательным или что-то в этом роде, рад, что вы это поняли.
3. Я обновлю свой предыдущий ответ, чтобы никто не запутался позже
4. Нет, это не так. Но сейчас ищу чистое решение. Было бы здорово, если бы вы могли использовать решение
ListSerialzer
.5. Я бы посоветовал, пожалуйста, протестировать ваше решение и опубликовать его только после того, как оно заработает. Я вижу много ошибок во втором решении.