如何在Django API中批量更新多条记录(批量更新)

6

我需要在一个请求中更新许多文章中的分类。ArticleViewSet 中有以下内容:

def get_serializer_class(self):
    if self.action in ['partial_update', 'update']:
        return ArticlePostSerializer
    return ArticleSerializer

所以需要更改ArticlePostSerializer

以下是我的序列化代码:

class ArticleShortCategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = Category
        fields = 'id', 'name'


class ArticleSerializer(serializers.ModelSerializer):
    categories = serializers.SerializerMethodField()

    def get_categories(self, obj):
        return ArticleShortCategorySerializer(obj.categories, many=True).data

    class Meta:
        model = Article
        read_only_fields = 'id'
        fields = ('categories', 'text') + read_only_fields


class ArticlePostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Article
        fields = 'id', 'categories', 'text'

我尝试添加:

class ArticlePostListSerializer(serializers.ListSerializer):

并且

class Meta:
    list_serializer_class = ArticlePostListSerializer

但它不起作用。 如何更改此代码以进行多次更新。 我的JSON请求

{
    [id: 90, categories: [10,12,14]],
    [id: 93, categories: [10,12,14]],
    [id: 95, categories: [10,12,14]]
}

使用创建混合类。 - Anup Yadav
使用create mixins.CreateModelMixin在视图中,并在序列化器中重写Create方法。或者您可以在get_queryset中完成更新工作,但这是一个不好的想法。 - Anup Yadav
@AnupYadav,你能给我一个示例代码吗?我是Django的新手。我需要更新记录,所以我应该使用mixins.CreateModelMixin还是mixins.UpdateModelMixin - pelcomppl
请使用以下任何一种方式,并检查答案中的示例,因为在评论中它无法正确显示。 - Anup Yadav
@AnupYadav,所以我不需要在ArticleViewSet中做任何更改吗?只改变序列化程序就足够了吗? - pelcomppl
实际上我不知道你的ArticleViewSet,但是我已经在代码中添加了我的View set,请检查一下。 - Anup Yadav
3个回答

1
这是您请求的CreateMixins或UpdateMixins的示例。
======================= 视图 ===============================
class OrderCreate(mixins.CreateModelMixin,viewsets.GenericViewSet):
    pagination_class = None

    def get_queryset(self):
        return []

    def get_serializer_class(self):
        return serializers.OrderSerializer

======================= 序列化器 =============================

class OrderDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = crm_models.OrderDetail
        fields = (
                'product',
                'quantity',
                'rate_per_unit',
                'order_quantity'
                )

class OrderSerializer(serializers.ModelSerializer):
    order_details = OrderDetailSerializer(many = True)
    class Meta:
        model = crm_models.OrderMaster
        fields = (
                'order',
                'invoice_number',
                'client',
                'beat_check',
                'target_customer',
                'order_editor_client_employee',
                'order_marked',
                'order_saved',
                'edit_marked',
                'edit_saved',
                'adhoc',
                'order_details'
                )

    def create(self, validated_data,*args,**kwargs):
        ordersdetails_data = validated_data.pop('order_details')
        user = None
        request = self.context.get("request")
        if request and hasattr(request, "user"):
            user = request.user
            validated_data['client'] = user.client
            validated_data['order_editor_client_employee'] = user
            validated_data['adhoc'] = validated_data['adhoc'] if 'adhoc' in validated_data else False

        orderObj = super(OrderSerializer, self).create(validated_data,*args,**kwargs)
        orderdetails = []
        for details in ordersdetails_data:
            orderdetails.append(crm_models.OrderDetail(
                product= details['product'],
                quantity = details['quantity'],
                rate_per_unit = details['rate_per_unit'],
                order_quantity = details['order_quantity'],
                order = orderObj
            ))
        crm_models.OrderDetail.objects.bulk_create(orderdetails)
        return orderObj

在更新视图功能中,函数名称将会更改为update,您可以在此找到更多文档:http://www.django-rest-framework.org/api-guide/generic-views/#createmodelmixin

我理解你的代码可以将OrderDetail添加到Order中。但是我需要更新多个Order记录。 - pelcomppl
你可以使用我的逻辑来实现这个,大多数情况下你需要进行原始工作,就像我所做的那样,基本上你需要获取请求字典,在其中通过JSON获取客户端发布的所有数据。你甚至可以在那里添加自定义验证。 - Anup Yadav
我应该发送这个请求的URL是什么 - 与单个更新(带有ID)相同吗? - pelcomppl

1
我发现 K. Moe 对于这个问题的回答:Django Rest Framework POST Update if existing or create 更易于理解和实现。您只需要在序列化器中添加一个 create 方法,并在视图中使用 mixins.CreateModelMixin、generics.GenericAPIView。然后,您可以使用 POST 请求来创建和更新存储在数据库中的数据,而不是使用 PUT/PATCH。以下是我的视图代码:
class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):    
    def post(self, request, format=None):
        is_many = isinstance(request.data, list)

        if is_many:
            serializer = ZipCodeSerializer(data=request.data, many=True)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        else:
            serializer = ZipCodeSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

1

@Greg Holst,为什么会有这么多重复的内容?为什么不这样做:

class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):
    def post(self, request, format=None):
        serializer = ZipCodeSerializer(data=request.data, many=isinstance(request.data, list))
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

无论如何,这只对我创建新对象有效,无法一次性地创建或更新(它不断告诉我这些对象已经存在),所以我做了以下操作:

class ContributorSyncListAPIView(ListAPIView):
    permission_classes = (isSuperUserPermission,)
    allowed_methods = ("GET", "PUT")
    lookup_field = "airtable_id"
    serializer_class = ContributorSyncSerializer  # Doesn't get used in Put, just in Get.
    model = Contributor

    def get_queryset(self):
        return self.model.objects.all()

    def put(self, request, format=None):
        objects = list(request.data)
        # objects is a list of OrderedDicts
        try:
            for o in objects:
                try:
                    instance = self.model.objects.get(
                        **{self.lookup_field: o.get(self.lookup_field)}
                    )
                    for key, value in o.items():
                        setattr(instance, key, value)
                except self.model.DoesNotExist:
                    instance = self.model(**o)
                instance.save()
            return Response(objects, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)

请注意,我上面的代码非常轻量级,因为它是为一个超级用户从一个环境同步模型而设计的,不同的环境具有相同的代码库;所以假设数据在输入第一个环境时已经过验证。对于任何其他目的,您都需要进行更多的验证。但这就是我必须逐个处理可能需要创建或更新的对象列表的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接