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

Django REST Framework中如何跨Serializer验证嵌套请求数据?

问题描述

数据库中有两张产品相关表:Product 存储产品基础信息,ProductStock 存储价格、数量等库存数据。计划向服务器发送如下嵌套结构的请求数据:

{
    "name":"product name",
    "brand":"socialcodia",
    "product_stock":{
        "price":"100",
        "quantity":"50"
    }
}

目前已实现 ProductSerializer 验证产品基础信息,但不清楚如何验证嵌套的 product_stock 数据,希望在 ProductSerializer 中导入 ProductStockSerializer,完成嵌套数据的验证与关联保存。


解决方案

1. 修改 ProductSerializer,嵌套子序列化器

在父序列化器中声明 ProductStockSerializer 字段,用于接收嵌套的库存数据,同时重写 create 方法处理关联对象的创建逻辑:

from rest_framework import serializers
from .models import Product, Medical
from .serializers import ProductStockSerializer  # 导入库存序列化器

class ProductSerializer(serializers.ModelSerializer):
    medical = serializers.CharField(read_only=True)
    id = serializers.CharField(read_only=True)
    is_visible = serializers.CharField(read_only=True, default=True)
    # 添加嵌套的库存序列化器,write_only=True 表示仅用于接收请求数据
    product_stock = ProductStockSerializer(write_only=True)

    class Meta:
        model = Product
        fields = ['id','medical','category','item','brand','is_visible', 'product_stock']

    def validate(self, attrs):
        request = self.context.get('request')
        attrs['medical'] = Medical.objects.get(pk=request.info.get('medical'))
        return attrs

    def create(self, validated_data):
        # 从验证后的数据中分离出库存数据
        stock_data = validated_data.pop('product_stock')
        # 创建产品主记录
        product = Product.objects.create(**validated_data)
        # 给库存数据关联已创建的产品
        stock_data['product'] = product
        # 传递上下文给库存序列化器,执行验证并保存
        stock_serializer = ProductStockSerializer(data=stock_data, context=self.context)
        stock_serializer.is_valid(raise_exception=True)
        stock_serializer.save()
        return product

2. 修复 ProductStockSerializer 的验证逻辑

原代码存在语法错误和不合理的类型判断,修正后如下:

from rest_framework import serializers
from .models import ProductStock
from medical.models import Medical

class ProductStockSerializer(serializers.ModelSerializer):
    medical = serializers.CharField(read_only=True)
    
    class Meta:
        model = ProductStock
        fields = ['medical','distributer','product','variant','batch','purchase_price','price','quantity','location','low_stock','expire_date']
        # 标记请求中必传的字段
        extra_kwargs = {
            'price': {'required': True},
            'quantity': {'required': True}
        }

    def validate(self, attrs):
        request = self.context.get('request')
        attrs['medical'] = Medical.objects.get(pk=request.info.get('medical'))
        
        # 验证批次号(若批次号为非必填,保留if判断;必填则直接验证)
        batch = attrs.get('batch')
        if batch:
            if len(batch) < 3 or len(batch) > 30:
                raise serializers.ValidationError("Invalid Batch Number")
        
        # 验证采购价(非必填则先判断是否存在)
        purchase_price = attrs.get('purchase_price')
        if purchase_price is not None:
            if not isinstance(purchase_price, (int, float)):
                raise serializers.ValidationError("Invalid Purchase Price")
        
        # 验证售价(修复原代码多逗号导致的元组问题)
        price = attrs.get('price')
        if not isinstance(price, (int, float)):
            raise serializers.ValidationError("Invalid Price")
        
        return attrs

3. 视图层无需额外修改

原 ProductViewSet 逻辑保持不变,序列化器会自动处理嵌套数据的接收、验证与保存:

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        if self.request.user.is_superuser:
            return Product.objects.all()
        return Product.objects.filter(medical=self.request.info['medical'])

核心逻辑说明

  • 嵌套序列化通过在父序列化器中声明子序列化器字段实现,write_only=True 避免返回数据时重复输出库存信息
  • 父序列化器的 create 方法中,需先创建主模型对象,再将其关联到子模型数据,最后通过子序列化器完成验证与保存
  • 必须传递父序列化器的 context 给子序列化器,确保子序列化器能获取到请求对象完成业务逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 01:24:31