DRF序列化器

1、概念

序列化器(Serializers)是 Django REST Framework 的核心组件,它负责将复杂的数据类型(如模型实例)转换为 Python 原生数据类型,进而可以轻松转换为 JSON、XML 等内容类型,同时也负责反序列化,将解析后的数据转换为复杂类型。

工作流程

  • 序列化流程:模型对象 (instance) → 序列化器 (Serializer(instance=obj)) → Python 字典 (.data) → 渲染器 → JSON 等格式响应。

  • 反序列化流程:请求数据 → 解析器 → Python 字典 → 序列化器 (Serializer(data=dict)) → 校验 (.is_valid()) → 有效数据 (.validated_data) → 保存为模型对象 (.save()) → 数据库

2、序列化器元类

class SerializerMetaclass(type):
    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  # 此处使用list拷贝一份,方便attrs.pop执行
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        # _creation_counter是Field的类变量,因此,该排序是按照field实例化(声明)的顺序排序
        fields.sort(key=lambda x: x[1]._creation_counter)

        # 确保继承后最终为父类+子类的字段,如果子类有覆盖,则以子类为准
        known = set(attrs)

        def visit(name):
            known.add(name)
            return name

        # 处理父类的序列化器
        base_fields = [
            (visit(name), f)
            for base in bases if hasattr(base, '_declared_fields')
            for name, f in base._declared_fields.items() if name not in known
        ]
        # Python 3.7后字典保持了插入顺序,此处可以认为该字典是base_fields + fields列表的顺序
        return dict(base_fields + fields)

    def __new__(cls, name, bases, attrs):
        # 序列化器都会有_declared_fields属性了
        attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
        return super().__new__(cls, name, bases, attrs)

3、序列化源码流程梳理

3.1 序列化器定义

以定义ModelSerializer为例,根据元类SerializerMetaclass创建序列化器类,增加_declared_fields属性,_declared_fields的值为base_fields(父类) + fields(当前类)

# models.py
from django.db import models

class User(models.Model):
    # 基础字段
    username = models.CharField(max_length=100, unique=True)

    # 时间字段
    created_at = models.DateTimeField(auto_now_add=True)
    
    # 选择字段
    USER_TYPE_CHOICES = [
        ('admin', 'Administrator'),
        ('user', 'Regular User'),
        ('guest', 'Guest User'),
    ]
    user_type = models.CharField(
        max_length=10, 
        choices=USER_TYPE_CHOICES, 
        default='user'
    )
# serializers.py

from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    """User 模型序列化器"""
    
    # 自定义方法字段 - 动态计算的只读字段
    account_age = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = [
            'id', 'username', 'user_type', 'created_at', 'account_age'
        ]
        read_only_fields = ['id', 'created_at']
    
    def get_account_age(self, obj):
        """计算账户存在时间"""
        from django.utils.timezone import now
        if obj.created_at:
            return (now() - obj.created_at).days
        return 0
    

3.2 序列化器实例化

调用BaseSerializer的__new__,如果many=True,则返回的是ListSerializer的对象,再执行__init__初始化

class Field:
    def __init__(self, *, ...source=None,...):
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1
        self.source = source

class BaseSerializer(Field):
    def __init__(self, instance=None, data=empty, **kwargs):
        # 初始化实例和数据
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        # 调用Field的__init__
        super().__init__(**kwargs)

    def __new__(cls, *args, **kwargs):
         # 处理 many=True 的情况,返回 ListSerializer
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)
    
    @classmethod
    def many_init(cls, *args, **kwargs):
        list_kwargs = {}
        # ...
        # 实例化model序列化器,作为参数child为后续实例化ListSerializer使用
        list_kwargs['child'] = cls(*args, **kwargs)
        # ...
        meta = getattr(cls, 'Meta', None)
        # 默认list_serializer_class为ListSerializer
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        # 实例化ListSerializer
        return list_serializer_class(*args, **list_kwargs)

3.3 序列化

序列化的触发流程为调用序列化器实例的.data时触发,基本流程为如下代码①->②->③->④->⑤->⑥


# ⑤⑤⑤⑤⑤
def get_attribute(instance, attrs):
    # 对于PK这种可以多级调用的,attrs为列表如 a.b.c,为[a,b,c]
    for attr in attrs:
        if isinstance(instance, Mapping):
            instance = instance[attr]
        else:
            instance = getattr(instance, attr)

        # 如果序列化器字段中设置source为可调用方法时,比如get_user_type_display
        if is_simple_callable(instance):
            # 方法调用结果赋值给instance
            instance = instance()


    return instance

class Field:
    # ④④④④④
    def get_attribute(self, instance):
        return get_attribute(instance, self.source_attrs)

class BaseSerializer(Field):

    # ②②②②②
    @property
    def data(self):
        if not hasattr(self, '_data'):
            if self.instance is not None and not getattr(self, '_errors', None):
                # 执行该分支
                self._data = self.to_representation(self.instance)
            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.validated_data)
            else:
                self._data = self.get_initial()
        return self._data


class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    
    # ①①①①①
    @property
    def data(self):
        # 调用父类的.data
        ret = super().data
        return ReturnDict(ret, serializer=self)
    

    # ③③③③③
    def to_representation(self, instance):
        ret = {}
        fields = self._readable_fields

        for field in fields:
            # 获取数据库中属性值
            attribute = field.get_attribute(instance)
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            # 调用各个字段进行序列化,此处以CharField为例
            ret[field.field_name] = field.to_representation(attribute)
        # 返回序列化后结果
        return ret

class CharField(Field):
    # ⑥⑥⑥⑥⑥
    def to_representation(self, value):
        return str(value)

4、校验

4.1 校验流程

执行.is_valid时进行校验流程:

  • 字段级别校验 - 单个字段的验证
  • 验证器运行 - 自定义验证器的执行
  • 对象级别校验 - 跨字段的复杂验证
  • 最终数据返回 - 返回验证后的数据

class Field:
    
    # 2.1 检查是否为空和必需
    def validate_empty_values(self, data):
        if self.read_only:
            return (True, self.get_default())

        if data is empty:
            if getattr(self.root, 'partial', False):
                raise SkipField()
            if self.required:
                self.fail('required')
            return (True, self.get_default())

        if data is None:
            if not self.allow_null:
                self.fail('null')
            elif self.source == '*':
                return (False, None)
            return (True, None)

        return (False, data)
    
    # 2.2.2 字段级别校验-内置的校验
    def run_validation(self, data=empty):
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
        # 转换为内部值
        value = self.to_internal_value(data)
        # 运行字段验证器
        self.run_validators(value)
        return value

    # # 2.3.1 执行校验器
    def run_validators(self, value):
        for validator in self.validators:
            if getattr(validator, 'requires_context', False):
                validator(value, self)
            else:
                validator(value)


class BaseSerializer(Field):

    # 1、校验入口
    def is_valid(self, *, raise_exception=False):
        self._validated_data = self.run_validation(self.initial_data)


class Serializer(BaseSerializer, metaclass=SerializerMetaclass):

    # 2、执行完整的校验流程
    def run_validation(self, data=empty):
        # 2.1 检查是否为空和必需
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
        # 2.2 将原始数据转换为内部表示,执行字段级别校验
        value = self.to_internal_value(data)
        # 2.3 执行校验器
        self.run_validators(value)
        # 2.4 全局钩子函数校验
        value = self.validate(value)
        return value
    
    # 2.2 将原始数据转换为内部表示,执行字段级别校验
    def to_internal_value(self, data):
        for field in fields:
            # 2.2.1 获取字段级别钩子函数
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            # 2.2.2 字段级别校验-内置的校验
            validated_value = field.run_validation(primitive_value)
            if validate_method is not None:
                # 2.2.3 字段级别校验-钩子函数校验
                validated_value = validate_method(validated_value)
            self.set_value(ret, field.source_attrs, validated_value)
        return ret

    # 2.3 执行校验器
    def run_validators(self, value):
        if isinstance(value, dict):
            to_validate = self._read_only_defaults()
            to_validate.update(value)
        else:
            to_validate = value
        # 2.3.1 执行校验器
        super().run_validators(to_validate)
    
    # 2.4 全局钩子函数,可以被覆盖后重新定义对属性进行组合校验
    def validate(self, attrs):
        return attrs

4.2 使用示例

4.2.1 字段级别校验

字段级别校验是校验流程的第一阶段,针对每个字段单独进行验证。

# 示例:字段级别校验
from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    username = serializers.CharField(max_length=100)
    email = serializers.EmailField()
    age = serializers.IntegerField(min_value=0, max_value=150)
    
    def validate_username(self, value):
        """字段级别的自定义验证"""
        if 'admin' in value.lower():
            raise serializers.ValidationError("用户名不能包含 'admin'")
        return value
    
    def validate_age(self, value):
        """另一个字段级别的验证"""
        if value < 18:
            raise serializers.ValidationError("年龄必须大于18岁")
        return value

字段级别校验的执行顺序:

  1. 字段的 to_internal_value() 方法将原始数据转换为Python数据类型
  2. 字段的 run_validation() 方法运行基本验证
  3. 如果定义了 validate_<field_name> 方法,则调用该方法
4.2.2 验证器运行

from rest_framework import serializers
from rest_framework.validators import UniqueValidator, UniqueTogetherValidator

class UserSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(
        validators=[
            UniqueValidator(queryset=User.objects.all())
        ]
    )
    
    class Meta:
        model = User
        fields = ['username', 'email', 'age']
        validators = [
            UniqueTogetherValidator(
                queryset=User.objects.all(),
                fields=['username', 'email']
            )
        ]
    
    def validate(self, data):
        """对象级别验证"""
        if data['username'] == data['email']:
            raise serializers.ValidationError("用户名和邮箱不能相同")
        return data

验证器类型:

  1. 字段验证器:在字段级别执行
  2. Meta验证器:在对象级别执行
  3. 自定义验证器:可以在不同阶段执行
4.2.3 对象级别校验

对象级别校验用于处理跨字段的复杂验证逻辑。


class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = ['product', 'quantity', 'discount_code', 'total_price']
    
    def validate(self, data):
        """
        对象级别验证示例
        这里可以访问所有字段的验证后数据
        """
        # 复杂业务逻辑
        # ...
        return data

4、序列化使用

常用字段参数

参数 说明 示例
read_only 字段仅用于序列化输出 read_only=True
write_only 字段仅用于反序列化输入 write_only=True
source 指定数据来源 source='get_role_display'
many 处理多个对象 many=True

4.1 模型关系


from django.db import models

class Department(models.Model):
    name = models.CharField(max_length=100)

class User(models.Model):
    USER_ROLE_CHOICES = (  # 定义choices
        (1, '普通用户'),
        (2, '管理员'),
    )
    name = models.CharField(max_length=100)
    role = models.IntegerField(choices=USER_ROLE_CHOICES, default=1)
    department = models.ForeignKey(Department, on_delete=models.CASCADE)  # 外键
    projects = models.ManyToManyField('Project')  # 多对多

class Project(models.Model):
    title = models.CharField(max_length=100)

4.2 序列化器定义

class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Department
        fields = ['id', 'name']

class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'title']

class UserWithProjectsSerializer(serializers.ModelSerializer):
    department = DepartmentSerializer(read_only=True)
    projects = ProjectSerializer(many=True, read_only=True)  # 嵌套序列化
    role_name = serializers.CharField(source='get_role_display', read_only=True)

    class Meta:
        model = User
        fields = ['id', 'name', 'role_name', 'projects', 'department']

序列化结果


queryset = models.User.objects.all()
        serializer = UserWithProjectsSerializer(queryset, many=True)
        return Response(serializer.data)

[
    {
        "id": 1,
        "name": "郭靖",
        "role_name": "普通用户",
        "projects": [
            {
                "id": 1,
                "title": "天水"
            },
            {
                "id": 2,
                "title": "地水"
            }
        ],
        "department": {
            "id": 1,
            "name": "开发"
        }
    },
    {
        "id": 2,
        "name": "黄蓉",
        "role_name": "管理员",
        "projects": [
            {
                "id": 1,
                "title": "天水"
            }
        ],
        "department": {
            "id": 2,
            "name": "测试"
        }
    },
    {
        "id": 3,
        "name": "洪七公",
        "role_name": "管理员",
        "projects": [
            {
                "id": 2,
                "title": "地水"
            }
        ],
        "department": {
            "id": 2,
            "name": "测试"
        }
    }
]
posted @ 2025-08-30 17:24  xclic  阅读(2)  评论(0)    收藏  举报