You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Rest Framework:基于ModelViewSet数据更新ModelSerializer对象求优解

Hey there! Let's take your PurchaseOrderApproverViewSet's approve action and make it cleaner, more robust, and aligned with Django REST Framework (DRF) best practices. The main issues with your current code are lack of data validation, improper serializer usage, and no clear error handling—here's how to fix all that:

1. First, Ensure Your Serializer is Properly Configured

Make sure your PurchaseOrderApproverSerializer includes all fields you need to update (like approval status, approval date, etc.) and follows DRF's standard structure:

from rest_framework import serializers
from .models import PurchaseOrderApprover
from django.utils import timezone

class PurchaseOrderApproverSerializer(serializers.ModelSerializer):
    class Meta:
        model = PurchaseOrderApprover
        fields = ['id', 'purchase_order', 'approver', 'is_approved', 'approval_date']
        read_only_fields = ['id']  # Mark fields that shouldn't be edited via API

    def update(self, instance, validated_data):
        # Add custom business logic here (e.g., auto-set approval timestamp)
        instance.is_approved = validated_data.get('is_approved', instance.is_approved)
        instance.approval_date = validated_data.get('approval_date', timezone.now())
        instance.save()

        # Optional: Update related PurchaseOrder status if needed
        # instance.purchase_order.status = 'approved'
        # instance.purchase_order.save()

        return instance

2. Refactor the Approve Action in Your ViewSet

Instead of directly pulling data from request.data (which is unsafe and unvalidated), use DRF's built-in serializer tools to handle validation, fetching, and updating:

from rest_framework import status
from rest_framework.response import Response
from rest_framework import viewsets
from .models import PurchaseOrderApprover
from .serializers import PurchaseOrderApproverSerializer

class PurchaseOrderApproverViewSet(viewsets.ModelViewSet):
    queryset = PurchaseOrderApprover.objects.all()
    serializer_class = PurchaseOrderApproverSerializer  # Set default serializer

    @action(methods=['POST'], detail=False)
    def approve_purchase_order(self, request):
        # Step 1: Validate incoming request data with your serializer
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # Auto-returns 400 if invalid

        # Step 2: Fetch the existing PurchaseOrderApprover record
        # Use validated_data instead of raw request.data for safety
        purchase_order = serializer.validated_data['purchase_order']
        approver = serializer.validated_data['approver']
        
        purchase_order_approver = PurchaseOrderApprover.objects.filter(
            purchase_order=purchase_order,
            approver=approver
        ).first()

        if not purchase_order_approver:
            return Response(
                {"detail": "No matching purchase order approver record found."},
                status=status.HTTP_404_NOT_FOUND
            )

        # Step 3: Update the record using the serializer's update method
        # Use partial=True to allow updating only specific fields
        update_serializer = self.get_serializer(
            purchase_order_approver,
            data=request.data,
            partial=True
        )
        update_serializer.is_valid(raise_exception=True)
        update_serializer.save()

        return Response(update_serializer.data, status=status.HTTP_200_OK)

3. Key Improvements Breakdown

  • Safe Data Handling: Using validated_data ensures you're only working with data that passed DRF's validation rules (no typos, wrong data types, or missing required fields).
  • Reusable Logic: Moving business logic to the serializer's update method keeps your viewset clean and makes the logic reusable across other parts of your app.
  • Proper Error Responses: Clear 404 and 400 responses give frontend developers meaningful feedback instead of silent failures.
  • DRF Best Practices: Using self.get_serializer() instead of manual serializer instantiation inherits your viewset's configuration (like permissions, authentication, and context).

4. Bonus: Make It More RESTful (Optional)

If this approval action targets a single PurchaseOrderApprover record, consider using a detail action instead. This way, you don't need to pass purchase_order and approver in the request body—you'll use the record's ID from the URL:

@action(methods=['POST'], detail=True)
def approve(self, request, pk=None):
    # Fetch the record directly using DRF's get_object()
    purchase_order_approver = self.get_object()
    
    update_serializer = self.get_serializer(
        purchase_order_approver,
        data=request.data,
        partial=True
    )
    update_serializer.is_valid(raise_exception=True)
    update_serializer.save()

    return Response(update_serializer.data)

This changes your endpoint to /purchase-order-approvers/<pk>/approve/, which follows REST conventions more closely.

内容的提问来源于stack exchange,提问作者cxstam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:19:32