Django REST Framework serializer
Django REST Framework Serializer 入门指南
Serializer 是 Django REST Framework (DRF) 中最核心的组件之一。它负责将复杂的数据类型(如 Django 模型实例或查询集)转换为 Python 原生数据类型,然后可以轻松渲染为 JSON、XML 或其他内容类型。Serializer 还提供了反序列化功能,在验证输入数据后将其转换回复杂类型。本教程将带你从零开始掌握 DRF Serializer 的使用。
1. 为什么需要 Serializer?
一个典型的 Web API 需要在数据库对象和 HTTP 请求/响应之间不断进行数据转换。例如,客户端发送一个 JSON 请求来创建新用户,后端需要将 JSON 数据反序列化为 Python 字典,经过验证后保存到数据库模型;当客户端请求用户数据时,后端需要将模型对象序列化为 JSON 返回。
DRF 的 Serializer 提供了一种声明式的方式来定义这些转换规则,集成了数据验证、字段定义、复杂关系处理等功能,让你的代码更清晰、可维护。
2. 创建第一个 Serializer
我们以一个简单的博客应用为例,定义一个基本的 Serializer 类。
2.1 定义模型
# models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
2.2 编写一个基本的 Serializer
在 serializers.py 中创建一个与模型无关的 Serializer(也可以直接基于模型定义,稍后介绍)。
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=200)
content = serializers.CharField()
created_at = serializers.DateTimeField(read_only=True)
通过声明与模型字段一一对应的字段,并指定字段类型、验证参数(如 max_length),PostSerializer 就具备了序列化与反序列化能力。
2.3 序列化数据
post = Post.objects.first()
serializer = PostSerializer(post)
print(serializer.data)
# 输出: {'id': 1, 'title': 'My Post', 'content': 'Hello world', 'created_at': '2025-03-18T10:00:00Z'}
如果序列化一个查询集,需要设置 many=True:
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
print(serializer.data)
2.4 反序列化与验证
反序列化时,需要调用 is_valid() 进行数据验证,然后通过 validated_data 获取清洗后的数据或通过 save() 保存到模型。
data = {'title': 'New Post', 'content': 'Fresh content'}
serializer = PostSerializer(data=data)
if serializer.is_valid():
validated_data = serializer.validated_data
# 手动保存
post = Post.objects.create(**validated_data)
也可以实现 .create() 和 .update() 方法,让 save() 直接返回实例对象:
class PostSerializer(serializers.Serializer):
# ... 字段定义
def create(self, validated_data):
return Post.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
instance.save()
return instance
调用 save() 即可:
serializer.save() # 执行 create 或 update
3. ModelSerializer:更快捷的方式
DRF 提供了 ModelSerializer,它能够自动基于 Django 模型生成字段和默认验证器,并自动实现简单的 create() 和 update() 方法。
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'created_at', 'category']
# 或者使用 fields = '__all__' 包含所有字段
ModelSerializer 会为 ForeignKey 关系自动生成 PrimaryKeyRelatedField。上述序列化器在序列化时会输出 category 字段的 id。如果你想控制输出,可以进一步自定义。
自定义 Meta 常用选项:
fields:需要序列化的字段列表。exclude:排除指定的字段。read_only_fields:只读字段列表,等同于在字段上添加read_only=True。extra_kwargs:为自动生成的字段提供额外参数,如write_only或验证器。
class Meta:
model = Post
fields = ['id', 'title', 'content', 'created_at', 'category']
read_only_fields = ['id', 'created_at']
extra_kwargs = {
'title': {'validators': []}, # 清除默认的唯一性验证器(假设你想自定义)
}
4. 深入字段与验证
DRF 提供了丰富的字段类型,每个字段都支持多种验证参数。
4.1 常用字段类型
CharField:字符串字段,支持max_length,min_length,allow_blank等。IntegerField:整数,支持min_value,max_value。FloatField、DecimalField(需要max_digits,decimal_places)DateTimeField、DateField、TimeFieldBooleanFieldEmailFieldURLFieldChoiceField:通过choices参数定义选项。FileField、ImageField:用于文件上传。ListField:列表字段,内含子字段。DictField、JSONField:处理字典或 JSON。
4.2 核心验证参数
| 参数 | 作用 |
|---|---|
required |
是否必须,默认 True。 |
default |
默认值,可为一个可调用对象。 |
allow_null |
是否允许 None 值。 |
read_only |
序列化时包含该字段,但反序列化时忽略。 |
write_only |
反序列化时接受该字段,但序列化时不输出。常用于密码。 |
validators |
指定额外的验证器函数列表。 |
error_messages |
自定义错误消息字典。 |
4.3 字段级验证与对象级验证
除了自动生成的验证(如类型、长度等),你可以在 Serializer 中添加自定义验证方法。
字段级验证:定义 validate_<field_name> 方法。
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content']
def validate_title(self, value):
if 'badword' in value.lower():
raise serializers.ValidationError("标题包含敏感词。")
return value
对象级验证:需要访问多个字段时,重写 validate() 方法。
def validate(self, data):
if data['title'] == data['content']:
raise serializers.ValidationError("标题和内容不能相同。")
return data
自定义验证器:还可以创建可复用的外部验证函数,并添加到字段的 validators 参数中。
def no_spam(value):
if 'spam' in value:
raise serializers.ValidationError("发现垃圾内容")
class PostSerializer(serializers.ModelSerializer):
title = serializers.CharField(validators=[no_spam])
5. 序列化关系与嵌套
博客文章通常包含分类、评论等关系,你需要根据 API 需求决定如何展示这些关系。
5.1 使用相关字段
DRF 关系字段如下:
PrimaryKeyRelatedField:返回关联对象的主键,默认行为。StringRelatedField:返回关联对象的__str__方法结果。SlugRelatedField:通过关联对象的某个 slug 字段进行表示。HyperlinkedRelatedField:生成超链接(需要视图集配合)。Nested Serializer:完全嵌套序列化器,展示关联对象的全部字段。
示例:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(read_only=True) # 嵌套序列化
class Meta:
model = Post
fields = ['id', 'title', 'content', 'category']
此时获取一篇文章会得到嵌套的类别对象:
{
"id": 1,
"title": "My Post",
"content": "...",
"category": {
"id": 1,
"name": "Django"
}
}
5.2 可写的嵌套序列化器
默认情况下,嵌套序列化器是只读的。如果要支持创建或更新时同时处理关联对象,需要重写 create() 和 update() 方法。
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Post
fields = ['title', 'content', 'category']
def create(self, validated_data):
category_data = validated_data.pop('category')
category = Category.objects.create(**category_data)
post = Post.objects.create(category=category, **validated_data)
return post
一般建议谨慎使用可写嵌套,避免过度复杂化;多数情况下提供关联字段的选择器(如 slug 或 id)更符合 RESTful 风格。
5.3 反向关联
当需要展示文章的所有评论时,可以在 Meta 中不使用,而是通过 SerializerMethodField 或 related_name 自定义字段。
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['id', 'text']
class PostSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True) # 假设模型有 related_name='comments'
class Meta:
model = Post
fields = ['id', 'title', 'comments']
6. 动态字段与自定义逻辑
6.1 仅获取部分字段
有时需要根据请求上下文动态调整返回的字段,可以重写 __init__ 方法。
class DynamicFieldsSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
# 允许在 instantiation 时传入 fields 参数
fields = kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if fields is not None:
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
使用示例:
serializer = PostSerializer(post, fields=('id', 'title'))
6.2 SerializerMethodField
这个字段可以让你添加一个只读的、完全自定义的方法返回值。
class PostSerializer(serializers.ModelSerializer):
days_since_created = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ['id', 'title', 'created_at', 'days_since_created']
def get_days_since_created(self, obj):
from django.utils import timezone
return (timezone.now() - obj.created_at).days
6.3 传递上下文
很多时候你需要在序列化器中访问请求对象,例如根据当前用户过滤数据、生成绝对 URL 等。此时可以使用上下文。
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'is_author']
is_author = serializers.SerializerMethodField()
def get_is_author(self, obj):
request = self.context.get('request')
return request.user == obj.author
实例化时传入上下文:
serializer = PostSerializer(queryset, many=True, context={'request': request})
7. 序列化器的性能与优化
当处理大量数据时,需要注意以下几种优化策略:
- 使用
select_related和prefetch_related减少数据库查询。嵌套序列化会产生 N+1 问题,在视图中优化查询集至关重要。 - 避免深嵌套:过多的嵌套序列化器会引发大量数据库查询和计算,考虑使用扁平化结构或分页。
- 使用
source参数:有时字段名与序列化器输出不符,可以用source直接访问对象属性,避免新建方法。 - 只返回必要字段:通过
fields或动态字段减少序列化数据量。 - 使用
to_representation控制输出:如果你需要在序列化前对数据进行全局处理,可以重写该方法。
class PostSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'category_name']
这样就把外键类别名称直接放在了顶层字段中,避免嵌套查询(但需要在视图中 select_related('category'))。
8. 序列化器之外的替代方案:Serializer 作为验证工具
Serializer 不仅用于模型数据转换,它也适合对任意数据进行验证。例如,当你的 API 接受复杂的查询参数或非模型数据时,可以创建一个不绑定模型的 Serializer 来验证参数。
class SearchSerializer(serializers.Serializer):
q = serializers.CharField(required=False)
category_id = serializers.IntegerField(required=False)
date_from = serializers.DateField(required=False)
在视图中直接使用验证后的参数:
serializer = SearchSerializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
# 使用 serializer.validated_data 构建过滤器
9. 常见问题与最佳实践
- 何时使用
SerializervsModelSerializer?
优先使用ModelSerializer,除非需要完全自定义或模型关系复杂,再降级为Serializer类。 - 如何避免循环导入?
当两个 Serializer 互相引用时,可以使用rest_framework.serializers.Serializer基础类和字符串形式的模型引用(model = 'app.Model')或延迟导入。 - 密码等敏感字段应设为
write_only=True,防止在 GET 响应中泄露。 - 统一错误格式:DRF 默认的错误格式已经很友好,但你可以通过自定义异常处理器来统一 API 的错误输出。
- 版本控制:如果 API 改变,可以创建新的 Serializer 类,避免破坏现有客户端。
10. 总结
DRF Serializer 是构建 RESTful API 的基石,它提供了从模型到 JSON 图的无缝转换、强大的验证系统以及灵活的定制能力。理解并熟练运用 Serializer,你将能够快速构建出结构清晰、健壮可靠的 Web API。
建议动手实践:从最简单的 ModelSerializer 开始,逐步添加字段验证、关系嵌套、自定义方法,并尝试优化数据库查询。结合视图集和路由器,你的 Django 开发效率将会大幅提升。