【问题标题】:How to create multiple objects in serializers create method?如何在序列化程序创建方法中创建多个对象?
【发布时间】:2020-10-30 22:51:58
【问题描述】:

我正在尝试上传一个 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)

当我上传文件并发布它时,我收到了这个错误:

尝试在序列化程序 FileUploadSerializer 上获取字段 user 的值时出现 AttributeError。序列化程序字段可能命名不正确,并且与 list 实例上的任何属性或键都不匹配。原始异常文本是:“列表”对象没有属性“用户”。

但我可以看到表已成功填充到数据库中。我应该从FileUploadSerializercreate 方法返回什么?

【问题讨论】:

  • FileUploadSerializer.create() 应该只重新运行一个模型实例。
  • @ArakkalAbu 我需要批量创建对象。
  • @ArakkalAbu,顺便说一句,您可以返回multiple
  • 您可以在传递到端点的数据中添加一个 sn-p 吗?
  • 我正在将一个文件传递给 api 端点。然后从文件中读取数据并创建覆盖序列化程序类创建方法的对象。

标签: python django database django-rest-framework


【解决方案1】:

好的,在我自己尝试了一个示例之后,我能够重现错误,我对现在发生这种情况的原因有了更好的理解。

首先,我们把create()的实现放到视图类上

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... 等的原始错误发生是因为 FileUploadView 中的 create() 正在返回 serializer.data,它期望字段 userfiledatacreate() 上的 FileUploadSerializer 是返回一个对象列表,以便您现在了解为什么会发生这种情况。

您可以通过在 FileUploadView 上覆盖 create() 并使用您将创建的 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

【讨论】:

  • 我赞成发现问题。我也想通了,我想到的天真的解决方案是只从列表中返回一个对象:return work_data_objects[0]
  • 我认为返回所有创建的对象是强制性的,很高兴你明白了。
  • 我会更新我之前的答案,这样以后没人会感到困惑
  • 不,不是。但现在正在寻找一个干净的解决方案。如果您可以使用ListSerialzer 提出解决方案,那就太好了。
  • 我建议请测试您的解决方案并在它工作后才发布它。我可以在第二个解决方案中看到很多错误。
猜你喜欢
  • 2019-12-06
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 2012-05-24
  • 1970-01-01
相关资源
最近更新 更多