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
相关产品推荐
相关产品推荐

