两种数据:
- 便于传输的数据:JSON、XML、……
- 语言内置对象:Python Native Object
相互转换的过程:
- serialize: 将语言内置对象转换为便于传输的数据
- deserialize: 将便于传输的数据转换为语言内置对象
DRF 的 Serializers 实现了以上两个功能:
Serializer
实现了语言对象与序列化数据之间的通用转换方法ModelSerializer
进一步为模型实例和查询集合实现了接口
Serializer 用来序列化/反序列化普通对象
serialize & deserialize
我们先定义一个普通类:
from datetime import datetime
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
然后我们定义一个 Serializer 类,这个类类似于表单数据对象(Django Form):
from rest_framework import Serializers
class CommentSerializer(serializer.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
注意,上面定义的 Comment 类和 CommentSerializer 类在语法上是完全不相干的。
然后我们可以用 CommentSerializer 来序列化/实例化 Comment 对象:
serialize
serializer = CommentSerializer(comment)
# serializer = CommentSerializer(instance=comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
deserialize
data = {'email': 'leila@example.com', 'content': 'foo bar', 'created': datetime.now()}
serializer = CommentSerializer(data=data)
serializer.is_valid()
# if True
serializer.validated_data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
serialize 将 对象实例 转换成 序列数据,而 deserialize 按照预先定义的规则(*Field)对 序列数据 进行校验,校验成功时可通过.validated_data
获取到数据。
deserialize & instantiate
deserialize 过程并不会主动将序列数据实例化成对象,因为序列化器(CommentSerializer)并不直接持有对象类(Comment)的引用。
如果需要在 deserialize 时自动实例化,即将 deserialize 过程升级为 instantiate 过程,我们需要单独为序列化器实现.create()
方法和.update()
方法。
在这两个方法中,我们手动创建实例对象,或者手动修改实例对象的属性:
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
def create(self, validated_data):
return Comment(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
return instance
我们可以看到:
create()
方法接收经过验证的数据字典作为参数,返回一个实例对象update()
方法接收实例对象和经过验证的数据字典作为参数,对实例属性经过适当修改后返回了修改后的实例
通过复写这两个增、改方法,现在序列化器直接持有了实例类的引用。
现在使用序列化器进行 deserialize 后,调用其save()
方法可以直接获得持有的实例对象的引用:
data = {'email': 'leila@example.com', 'content': 'foo bar', 'created': datetime.now()}
serializer = CommentSerializer(data=data)
if serializer.is_valid():
print(serializer.validated_data)
comment = serializer.save()
save()
方法通常在数据库模型类的实例对象反序列化时用到,当我们从序列化数据创建一条记录时,create()
方法通常这样写:
def create(self, validated_data):
return Comment.objects.create(**validated_data)
save()
方法和update()
方法的自定义实现可以根据自己对序列化器的功能需求进行修改。
序列化器对数据库模型数据的序列化/反序列化已经在ModelSerialize
类中实现了,不需要我们重复造轮子。
在创建/修改数据库模型实例时,save()
方法还可以接收额外的关键字参数,这些关键字参数都会直接被添加到validated_data
中,保存到数据库。
例如我们可以将设置 owner 的操作放在save()
方法中:
serialize.save(owner=request.user)
ms 平台中关于 owner 的设置就可以从前端移除,改成这种形式!
custom save method
Serializer 的 save 一般用于序列化器接受序列化数据并验证数据之后,执行某些操作,在 DRF 中一般是保存实例数据到数据库。
但是有些情况下,我们可以扩展 save 的概念,它不一定需要永久保存数据,也可以是对验证的数据进行一些特定的操作,然后丢弃它,或者在保存之前另外做一些事情。
例如在一个 Contact 模型中,我们希望 Serializer 验证请求数据后,直接向用户发送邮件,而不是将该数据保存到数据库:
class Contact(serializers.Serializer):
email = serializer.EmailField()
message = serializer.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
需要注意的是,save 方法是从已经验证的数据字典(validated_data
)中取数据。
validation
反序列化时才需要验证
Serializer 在反序列化数据时,必须先调用is_valid()
方法验证数据。
只有经过验证后,才能通过validated_data
属性拿到验证后的数据。
验证错误时的原因
验证失败时,可以通过errors
属性获取验证失败的原因,其格式是:
{ [field: string]: string[] }
当不是字段验证出错时,errors 字典的键是"non_field_errors"
,这个键可以通过NON_FIELD_ERRORS_KEY
配置。
当反序列化多个数据字典时,errors 会以数组形式返回,每个元素代表每个序列数据的验证结果。
验证失败时直接抛出异常
is_valid(raise_exception=True)
会在数据验证出错时直接抛出异常,这个异常会被 DRF 自动处理,返回HTTP 400 Bad Request
响应,表示验证失败是由请求数据不正确引起的。
字段级别的验证
为序列化器添加validate_<field_name>()
方法可以直接对指定的字段进行验证,该方法
- 验证失败时应该抛出
serializers.ValidationError
异常,并注明异常原因 - 最后返回经过验证的值
例如下面的例子验证手机号必须是11位:
from test_framework import serializers
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=32)
cellphone = serializers.CharField(max_length=11)
def validate_cellphone(self, val: str):
if len(val) != 11:
raise serializers.ValidationError("cellphone must be 11 bits")
return val
对于可选的字段(required=False
)只有在序列化数据包含该字段时才会触发验证。
对象级别的验证
如果需要联合几个字段一起验证,应该在对象级别进行验证,重写validate()
方法可实现在对象级别进行验证:
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=32)
cellphone = serializers.CharField(max_length=11)
def validate(self, data: Dict):
...
入参 data 是一个数据字典,验证失败抛出 ValidationError,验证成功返回数据字典。
函数验证器
除了添加实例方法validate_<field_name>()
对字段进行验证外,还可以在定义字段时指定一系列的函数验证器:
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
验证函数直接以待验证的值作为参数,验证失败抛出 ValidationError,并且验证成功可以不需要返回值
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
可复用的验证器
DRF 提供了可复用的验证器:
from rest_framework import validators
高级用法,此处不表,可参考文档:https://www.django-rest-framework.org/api-guide/validators/#validators
评论区