DRF模型继承序列化(读写)、嵌套序列化及OPTIONS方法配置咨询
嘿,我来帮你搞定这两个DRF的问题,都是开发中经常碰到的场景,咱们一个个来:
Django的模型继承分三种类型,每种的序列化处理方式略有不同,我分别给你梳理:
抽象基类(Abstract Base Classes)
这是最常用的继承方式——父类只是定义通用字段,不会生成数据库表,子类继承所有字段并生成自己的表。序列化起来最省心:
- 先写父类序列化器,包含抽象模型的通用字段
- 子类序列化器直接继承父类,再追加自身模型的特有字段
- 读写操作和普通
ModelSerializer完全一致,因为子类模型已经包含所有字段,不需要额外处理关联
示例代码:
# 模型定义 from django.db import models class BaseItem(models.Model): name = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) class Meta: abstract = True class Book(BaseItem): author = models.CharField(max_length=100) class Movie(BaseItem): director = models.CharField(max_length=100) # 序列化器定义 from rest_framework import serializers class BaseItemSerializer(serializers.ModelSerializer): class Meta: model = BaseItem fields = ['name', 'created_at'] class BookSerializer(BaseItemSerializer): class Meta(BaseItemSerializer.Meta): model = Book fields = BaseItemSerializer.Meta.fields + ['author'] class MovieSerializer(BaseItemSerializer): class Meta(BaseItemSerializer.Meta): model = Movie fields = BaseItemSerializer.Meta.fields + ['director']
多表继承(Multi-table Inheritance)
这种情况父类会生成独立表,子类通过自动创建的OneToOneField关联父类,数据分布在两张表里。序列化需要处理嵌套结构:
读取操作
直接用嵌套序列化器把父类数据包含进来就行:
class BaseItemSerializer(serializers.ModelSerializer): class Meta: model = BaseItem fields = ['name', 'created_at'] class BookSerializer(serializers.ModelSerializer): baseitem = BaseItemSerializer() # 嵌套父类序列化器 class Meta: model = Book fields = ['id', 'author', 'baseitem']
写入操作(Create/Update)
因为涉及两张表的联动,要么手动重写create/update方法,要么用drf_writable_nested简化操作:
from drf_writable_nested.serializers import WritableNestedModelSerializer class BookSerializer(WritableNestedModelSerializer): baseitem = BaseItemSerializer() class Meta: model = Book fields = ['id', 'author', 'baseitem'] # 手动实现create(如果不用WritableNested的话) def create(self, validated_data): baseitem_data = validated_data.pop('baseitem') baseitem = BaseItem.objects.create(**baseitem_data) book = Book.objects.create(baseitem=baseitem, **validated_data) return book # 手动实现update(如果不用WritableNested的话) def update(self, instance, validated_data): baseitem_data = validated_data.pop('baseitem', None) if baseitem_data: baseitem_serializer = BaseItemSerializer(instance.baseitem, data=baseitem_data) if baseitem_serializer.is_valid(): baseitem_serializer.save() return super().update(instance, validated_data)
用WritableNestedModelSerializer的话,它会自动处理嵌套的创建和更新,不用手动写上述逻辑,前提是模型的关联关系配置正确。
代理模型(Proxy Models)
代理模型只是父类的“视图层”,不生成新表,只是改变查询逻辑或行为。序列化器直接复用父类的即可,或者简单继承后修改model:
class PremiumItem(BaseItem): class Meta: proxy = True ordering = ['-created_at'] class PremiumItemSerializer(BaseItemSerializer): class Meta(BaseItemSerializer.Meta): model = PremiumItem
读写操作和父类完全一致,因为数据存在同一张表里。
既然你已经在用drf_writable_nested的WritableNestedModelSerializer,那可以充分利用它的能力来实现嵌套读写,同时DRF默认就能支持OPTIONS获取字段信息,咱们一步步来:
1. elements字段的最优实现
假设你的RootModel和ElementModel是一对多关系(比如Root包含多个Element),按以下步骤配置:
第一步:定义Element序列化器
先为子模型写序列化器,如果Element本身没有嵌套,继承ModelSerializer就行;如果有嵌套,就继承WritableNestedModelSerializer:
class ElementSerializer(WritableNestedModelSerializer): class Meta: model = ElementModel fields = ['id', 'name', 'value'] # 替换成你的模型字段
第二步:定义RootSerializer
让RootSerializer继承WritableNestedModelSerializer,然后把elements字段设为ElementSerializer(many=True):
class RootSerializer(WritableNestedModelSerializer): elements = ElementSerializer(many=True) class Meta: model = RootModel fields = ['id', 'title', 'elements'] # 包含Root自身字段和嵌套的elements
这样配置后:
- GET请求:自动序列化所有关联的Element数据,返回嵌套结构
- POST请求:可以直接传入包含
elements数组的JSON,扩展会自动创建Root实例和对应的Element实例(注意要确保ElementModel的外键关联正确,比如root = models.ForeignKey(RootModel, related_name='elements', on_delete=models.CASCADE)) - PUT请求:自动更新Root实例,同时处理elements的增删改——存在
id的元素会更新,没有id的会创建,未出现在数组里的元素默认会被删除(如果想保留未提交的元素,可以重写update方法自定义逻辑)
自定义写入逻辑(可选)
如果不想删除未提交的元素,可以重写update方法:
def update(self, instance, validated_data): elements_data = validated_data.pop('elements', []) # 先更新Root自身字段 instance = super().update(instance, validated_data) # 只更新/创建传入的elements,不删除原有未传入的 for element_data in elements_data: element_id = element_data.get('id') if element_id: element = ElementModel.objects.get(id=element_id, root=instance) ElementSerializer(element, data=element_data).save() else: ElementModel.objects.create(root=instance, **element_data) return instance
2. OPTIONS方法获取相关信息
DRF默认的OPTIONS响应会自动包含序列化器的所有字段信息,包括嵌套的elements字段。如果你的视图集继承ModelViewSet,不需要额外配置,发送OPTIONS请求到Root的端点,就能看到:
- 允许的HTTP方法(GET/POST/PUT等)
- 每个字段的类型、是否必填等细节,包括
elements数组里的子字段
如果想自定义OPTIONS响应内容(比如添加示例请求格式),可以重写视图集的options方法:
from rest_framework import viewsets from rest_framework.response import Response class RootViewSet(viewsets.ModelViewSet): queryset = RootModel.objects.all() serializer_class = RootSerializer def options(self, request, *args, **kwargs): response = super().options(request, *args, **kwargs) # 添加自定义示例数据 response.data['example_request'] = { "title": "Sample Root", "elements": [ {"name": "Element 1", "value": "foo"}, {"name": "Element 2", "value": "bar"} ] } return response
内容的提问来源于stack exchange,提问作者Etienne

