Django REST Framework serializer

FreeGuideOnline 16阅读 2026-07-12

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
  • FloatFieldDecimalField(需要 max_digits, decimal_places
  • DateTimeFieldDateFieldTimeField
  • BooleanField
  • EmailField
  • URLField
  • ChoiceField:通过 choices 参数定义选项。
  • FileFieldImageField:用于文件上传。
  • ListField:列表字段,内含子字段。
  • DictFieldJSONField:处理字典或 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

一般建议谨慎使用可写嵌套,避免过度复杂化;多数情况下提供关联字段的选择器(如 slugid)更符合 RESTful 风格。

5.3 反向关联

当需要展示文章的所有评论时,可以在 Meta 中不使用,而是通过 SerializerMethodFieldrelated_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_relatedprefetch_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. 常见问题与最佳实践

  • 何时使用 Serializer vs ModelSerializer
    优先使用 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 开发效率将会大幅提升。