DRF 嵌套序列化器 #
一、嵌套序列化器概述 #
1.1 什么是嵌套序列化器 #
嵌套序列化器允许在一个序列化器中包含另一个序列化器,用于处理复杂的数据结构和模型关系。
text
简单序列化器
┌─────────────────┐
│ Article │
│ - id │
│ - title │
│ - content │
└─────────────────┘
嵌套序列化器
┌─────────────────────────────────────┐
│ Article │
│ - id │
│ - title │
│ - content │
│ - category: ┌─────────────────┐ │
│ │ Category │ │
│ │ - id │ │
│ │ - name │ │
│ └─────────────────┘ │
│ - tags: ┌─────────────────────┐ │
│ │ [Tag, Tag, ...] │ │
│ └─────────────────────┘ │
└─────────────────────────────────────┘
1.2 应用场景 #
| 场景 | 说明 |
|---|---|
| 外键关系 | 文章所属分类 |
| 一对多关系 | 文章的评论列表 |
| 多对多关系 | 文章的标签 |
| 反向关系 | 用户的所有文章 |
二、外键关系(ForeignKey) #
2.1 基本外键序列化 #
python
from rest_framework import serializers
from .models import Category, Article
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class ArticleSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True)
class Meta:
model = Article
fields = ['id', 'title', 'content', 'category']
输出:
json
{
"id": 1,
"title": "文章标题",
"content": "文章内容",
"category": {
"id": 1,
"name": "技术"
}
}
2.2 外键写入 #
python
class ArticleSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True)
category_id = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all(),
source='category',
write_only=True
)
class Meta:
model = Article
fields = ['id', 'title', 'content', 'category', 'category_id']
2.3 SlugRelatedField #
使用指定字段表示外键:
python
class ArticleSerializer(serializers.ModelSerializer):
category = serializers.SlugRelatedField(
slug_field='name',
queryset=Category.objects.all()
)
class Meta:
model = Article
fields = ['id', 'title', 'category']
输出:
json
{
"id": 1,
"title": "文章标题",
"category": "技术"
}
2.4 StringRelatedField #
使用__str__方法表示外键:
python
class ArticleSerializer(serializers.ModelSerializer):
category = serializers.StringRelatedField()
class Meta:
model = Article
fields = ['id', 'title', 'category']
三、一对多关系 #
3.1 反向关系序列化 #
python
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['id', 'content', 'author', 'created_at']
class ArticleSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = ['id', 'title', 'comments']
输出:
json
{
"id": 1,
"title": "文章标题",
"comments": [
{
"id": 1,
"content": "评论内容",
"author": "张三",
"created_at": "2024-01-15T10:00:00Z"
}
]
}
3.2 使用related_name #
在模型中定义related_name:
python
class Comment(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name='comments'
)
content = models.TextField()
序列化器:
python
class ArticleSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = '__all__'
3.3 嵌套创建评论 #
python
class ArticleSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = '__all__'
def create(self, validated_data):
comments_data = validated_data.pop('comments', [])
article = Article.objects.create(**validated_data)
for comment_data in comments_data:
Comment.objects.create(article=article, **comment_data)
return article
四、多对多关系 #
4.1 基本多对多序列化 #
python
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name']
class ArticleSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = ['id', 'title', 'tags']
4.2 多对多写入 #
python
class ArticleSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
tag_ids = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
many=True,
source='tags',
write_only=True
)
class Meta:
model = Article
fields = ['id', 'title', 'tags', 'tag_ids']
def create(self, validated_data):
tags = validated_data.pop('tags', [])
article = Article.objects.create(**validated_data)
article.tags.set(tags)
return article
def update(self, instance, validated_data):
tags = validated_data.pop('tags', None)
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
if tags is not None:
instance.tags.set(tags)
return instance
4.3 SlugRelatedField多对多 #
python
class ArticleSerializer(serializers.ModelSerializer):
tags = serializers.SlugRelatedField(
slug_field='name',
many=True,
queryset=Tag.objects.all()
)
class Meta:
model = Article
fields = ['id', 'title', 'tags']
五、深层嵌套 #
5.1 多层嵌套 #
python
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email']
class CommentSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
class Meta:
model = Comment
fields = ['id', 'content', 'author']
class ArticleSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Article
fields = '__all__'
输出:
json
{
"id": 1,
"title": "文章标题",
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
"comments": [
{
"id": 1,
"content": "评论内容",
"author": {
"id": 2,
"username": "user",
"email": "user@example.com"
}
}
]
}
5.2 使用depth参数 #
python
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
depth = 2
depth参数自动展开嵌套关系:
depth=0:只显示IDdepth=1:展开一层depth=2:展开两层
六、嵌套创建和更新 #
6.1 嵌套创建 #
python
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['product', 'quantity', 'price']
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'items', 'total']
def create(self, validated_data):
items_data = validated_data.pop('items')
order = Order.objects.create(**validated_data)
for item_data in items_data:
OrderItem.objects.create(order=order, **item_data)
return order
6.2 嵌套更新 #
python
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'items', 'total']
def update(self, instance, validated_data):
items_data = validated_data.pop('items', None)
instance.customer = validated_data.get('customer', instance.customer)
instance.total = validated_data.get('total', instance.total)
instance.save()
if items_data is not None:
instance.items.all().delete()
for item_data in items_data:
OrderItem.objects.create(order=instance, **item_data)
return instance
6.3 部分更新嵌套 #
python
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'items', 'total']
def update(self, instance, validated_data):
items_data = validated_data.pop('items', None)
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
if items_data is not None:
existing_ids = set(instance.items.values_list('id', flat=True))
updated_ids = set()
for item_data in items_data:
item_id = item_data.get('id')
if item_id:
updated_ids.add(item_id)
OrderItem.objects.filter(id=item_id, order=instance).update(**item_data)
else:
OrderItem.objects.create(order=instance, **item_data)
instance.items.exclude(id__in=updated_ids).delete()
return instance
七、动态嵌套序列化 #
7.1 条件嵌套 #
python
class ArticleSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
class Meta:
model = Article
fields = '__all__'
def get_comments(self, obj):
request = self.context.get('request')
if request and request.query_params.get('include_comments') == 'true':
return CommentSerializer(obj.comments.all(), many=True).data
return None
7.2 按需展开 #
python
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
def to_representation(self, instance):
data = super().to_representation(instance)
request = self.context.get('request')
if request and request.query_params.get('expand') == 'category':
data['category'] = CategorySerializer(instance.category).data
if request and request.query_params.get('expand') == 'comments':
data['comments'] = CommentSerializer(
instance.comments.all(), many=True
).data
return data
7.3 可配置嵌套 #
python
class ArticleSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
expand = kwargs.pop('expand', [])
super().__init__(*args, **kwargs)
if 'category' not in expand:
self.fields['category'] = serializers.PrimaryKeyRelatedField(read_only=True)
if 'comments' not in expand:
self.fields.pop('comments', None)
class Meta:
model = Article
fields = '__all__'
使用:
python
serializer = ArticleSerializer(article, expand=['category', 'comments'])
八、嵌套验证 #
8.1 嵌套字段验证 #
python
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['product', 'quantity', 'price']
def validate_quantity(self, value):
if value <= 0:
raise serializers.ValidationError('数量必须大于0')
return value
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'items']
def validate_items(self, value):
if not value:
raise serializers.ValidationError('订单必须包含至少一个商品')
return value
8.2 跨字段验证 #
python
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'items', 'total']
def validate(self, data):
items = data.get('items', [])
total = data.get('total', 0)
calculated_total = sum(item.get('price', 0) * item.get('quantity', 0) for item in items)
if total != calculated_total:
raise serializers.ValidationError({
'total': f'订单总价{total}与商品总价{calculated_total}不一致'
})
return data
九、性能优化 #
9.1 使用prefetch_related #
python
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.prefetch_related('comments', 'tags')
serializer_class = ArticleSerializer
9.2 使用select_related #
python
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.select_related('category', 'author')
serializer_class = ArticleSerializer
9.3 组合使用 #
python
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.select_related(
'category', 'author'
).prefetch_related(
'comments__author', 'tags'
)
serializer_class = ArticleSerializer
9.4 限制嵌套深度 #
python
class ArticleSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
class Meta:
model = Article
fields = '__all__'
def get_comments(self, obj):
comments = obj.comments.all()[:10]
return CommentSerializer(comments, many=True).data
十、实际应用示例 #
10.1 博客文章完整序列化 #
python
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'avatar']
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name']
class CommentSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
replies = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = ['id', 'content', 'author', 'created_at', 'replies']
def get_replies(self, obj):
if obj.replies.exists():
return CommentSerializer(obj.replies.all(), many=True).data
return []
class ArticleDetailSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
category = CategorySerializer(read_only=True)
tags = TagSerializer(many=True, read_only=True)
comments = CommentSerializer(many=True, read_only=True)
comment_count = serializers.SerializerMethodField()
class Meta:
model = Article
fields = '__all__'
def get_comment_count(self, obj):
return obj.comments.count()
10.2 订单系统序列化 #
python
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'name', 'price', 'image']
class OrderItemSerializer(serializers.ModelSerializer):
product = ProductSerializer(read_only=True)
subtotal = serializers.SerializerMethodField()
class Meta:
model = OrderItem
fields = ['id', 'product', 'quantity', 'price', 'subtotal']
def get_subtotal(self, obj):
return obj.quantity * obj.price
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True, read_only=True)
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Order
fields = ['id', 'order_no', 'items', 'total', 'status', 'status_display', 'created_at']
十一、总结 #
本章学习了嵌套序列化器的使用:
- 外键关系:处理ForeignKey关系
- 一对多关系:处理反向关系
- 多对多关系:处理ManyToMany关系
- 深层嵌套:多层嵌套序列化
- 嵌套创建更新:实现嵌套数据的CRUD
- 性能优化:减少数据库查询
嵌套序列化器是处理复杂数据结构的关键,让我们继续学习自定义序列化!
最后更新:2026-03-28