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
字段级别校验的执行顺序:
- 字段的 to_internal_value() 方法将原始数据转换为Python数据类型
- 字段的 run_validation() 方法运行基本验证
- 如果定义了 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
验证器类型:
- 字段验证器:在字段级别执行
- Meta验证器:在对象级别执行
- 自定义验证器:可以在不同阶段执行
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": "测试"
}
}
]