DRF之序列化组件
【二】序列化组件
【1】序列化与反序列化的概念
-
序列化(Serialization):将对象转换为可传输或可存储的格式的过程。在序列化过程中,对象的属性和数据被转换为一个字节流或字符串,以便在网络上传输或保存到文件中。常见的序列化格式包括 JSON、XML、Protocol Buffers 等。序列化后的数据可以在不同的系统、编程语言或应用程序之间进行交换和共享。
-
反序列化(Deserialization):将序列化后的数据恢复为原始对象的过程。在反序列化过程中,从序列化格式(例如 JSON 字符串)中解析出对象的属性和数据,并重新构建原始对象。反序列化的过程与序列化过程相反,它将序列化后的数据转换回原始对象,以便进行进一步的处理、操作或显示。
-
简单来说:
-
序列化:将对象转换为可传输的格式
-
反序列化:从 JSON 字符串、XML 文档、字节流等中解析出对象的属性和数据。
-
【2】序列化类(Serializer)
【2.1】定义序列化类
# 定义序列化类需要继承drf.Serializer
from rest_framework import serializers
'''简单示例'''
class MySerializer(serializers.Serializer):
字段名 = serializers.字段类型(字段参数)
'''实例'''
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
def create(self, validated_data):
"""
根据提供的验证过的数据创建并返回一个新的`Snippet`实例。
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
根据提供的验证过的数据更新和返回一个已经存在的`Snippet`实例。
"""
instance.title = validated_data.get('title', instance.title)
return instance
- 序列化器类的第一部分定义了序列化/反序列化的字段。
create()
和update()
方法定义了在调用serializer.save()
时如何创建和修改完整的实例。
【2.2】常见字段及参数
【2.2.1】常用字段类型
字段 | 字段构造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
【2.2.2】选项参数
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
【2.2.3】通用参数
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
【3】序列化类的基本操作
【3.1】序列化
# 导入构建好的序列化类
from xx import Serializer
# 创建序列化类对象
ser = Serializer(instance=obj对象,data=request.data,...)
# 获取序列化后的数据 # 以json格式返回
ser.data
【3.1.1】序列化对象的常用参数
- instance:要序列化的模型实例。如果需要对现有对象进行序列化,则传递该实例。
- data:要反序列化的数据。如果需要从数据中创建对象,则传递该数据。通常用于创建或更新对象。
- context:上下文数据,可以在序列化器的各个方法中使用。通常用于在序列化器之间传递额外的信息。
- many:指定是否序列化多个对象。默认为 False。如果设置为 True,则可以序列化多个对象的查询集。
- partial:指定是否部分更新对象。默认为 False。如果设置为 True,则可以部分更新对象而不需要提供所有字段的值。
【3.2】反序列化校验
- 序列化器在反序列化时通常会执行一系列验证操作,以确保输入的数据符合预期的格式和约束。
- 这些验证功能可以帮助确保用户提供的数据是有效的,并且可以在存储到数据库之前进行预处理。
【3.2.1】校验级别
【3.2.1.1】字段级别的验证
class BookSerializer(serializers.Serializer):
name = serializers.CharField(min_length=5, max_length=32, error_messages={
'min_length': '最少不得少于5个字符',
'max_length': '最多不得多于32个字符'
})
price = serializers.IntegerField(min_value=10, max_value=999, error_messages={
'min_value': '最少不得少于10元',
'max_value': '最多不得多于999元'
})
publish = serializers.CharField(min_length=3, max_length=255, error_messages={
'min_length': '最少不得少于3个字符',
'max_length': '最多不得多于255个字符'
})
【3.2.1.2】自定制校验器validators
- 与forms组件一样,可以指定
validators
参数自定制验证器
def func(value):
'''验证数据格式'''
if xxx:
# 指定条件
# 抛出异常
raise ValidationError("错误信息")
return value
class MySerializer(serializers.Serializer):
# 可以指定多个验证器
name = serializers.CharField(max_length=32,validators=[func,func2...])
【3.2.1.3】钩子函数
- 局部钩子:
validate_字段(self,字段)
- 全局钩子:
validate(self,attrs)
- 校验失败 抛出指定异常
ValidationError
from rest_framework.exceptions import ValidationError
# 局部钩子
def validate_task_name(self, name: str):
if 'sb' in name:
# 抛出指定异常
raise ValidationError('不得包含侮辱性词汇')
# 注意需要返回该字段
return name
# 全局钩子
def validate(self, attrs):
if attrs.get('name') == attrs.get('publish'):
raise ValidationError('书名不得与出版社名一致')
# 需要返回字段
return attrs
【3.2.2】校验完成的数据 校验失败的错误信息
-
ser.is_valid()
:返回布尔值,True表示所有数据验证通过,False表示有错误数据- 通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应
-
ser.data
:包含了序列化后字段及其对应数值的字典,可以直接用于生成 JSON 或其他格式的响应数据 -
ser.errors
:校验失败的错误提示信息,由error_message
参数指定或使用默认
# views.py
'''不需要携带参数的视图类'''
class BookViewCR(APIView):
def get(self, request):
book_all = Book.objects.all()
# 将需要序列化的数据给 【instance】参数 # 如果是多条 需要传递 【many】参数
ser = BookSerializer(instance=book_all, many=True)
# 返回 一个数组【[{},{}]】 # 指定了many参数将视为多条数据 并按照restful规范返回数组格式
return Response({'code': 100,'msg': 'get','results': ser.data})
def post(self, request):
data = request.data
# 新增数据 # 直接将数据传入data参数中
ser = BookSerializer(data=data)
if ser.is_valid():
# 校验数据
# 校验通过 # 执行保存方法
ser.save()
return Response({'code': 100, 'msg': '添加成功', 'result': ser.data})
else:
# 校验失败
# 返回错误信息
return Response({'code': 101, 'msg': ser.errors})
'''需要携带 pk 参数 的视图类'''
class BookViewRUD(APIView):
# 查询指定pk数据
def get(self, request, tid):
# 获取对象
book = Book.objects.filter(pk=tid).first()
if not book:
return Response({'code': 101, 'msg': '当前书籍不存在'})
# 将查询到的对象传入instance
ser = BookSerializer(instance=book)
# 返回序列化后的数据
return Response({'code': 100, 'msg': f'查询指定【{tid}】成功', 'result': ser.data})
# 修改指定数据
def put(self, request, tid):
# 获取对象
book = Book.objects.filter(pk=tid).first()
if not book:
return Response({'code': 101, 'msg': '当前书籍不存在'})
# 将对象传入instance # 将需要更新的数据传入data
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
# 进行数据校验
# 数据校验通过调用save方法保存
ser.save()
return Response({'code': 100, 'msg': f'修改指定【{tid}】成功', 'result': ser.data})
else:
# 校验失败返回错误信息
return Response({'code': 101, 'msg': ser.errors, })
【3.2】反序列化保存 create
update
- 序列化器判断是新建对象还是更新对象的标准就是,是否传递了
instance
参数,数据将由data
参数接受
class xxx(serializers.Serializer):
# ...
def create(self, validated_data):
'''可以对校验过的数据进行再次处理'''
# 新增数据
book = Book.objects.create(**validated_data)
'''
title = validated_data.pop('title')
title += '爆款'
# 为标题加上爆款二字
Book.objects.create(**validated_data,title=title)
'''
return book
def update(self, instance, validated_data):
# 由于instance是一个对象,没办法直接使用 【queryset.update(**kwargs)】方法
# 所以需要使用反射的方法为对象更新属性
for key in validated_data.keys():
setattr(instance, key, validated_data.get(key))
instance.save()
# 将对象返回
return instance
【3.2.1】save(**kwargs)
- 对序列化器进行
save()
保存时,可以额外传递数据,这些数据可以在create()
和update()
中的validated_data
参数获取到
# views.py
class MyView(APIView):
def post(self,request):
ser = Ser(data=reuqest.data)
ser.is_valid(raise_exception=True)
ser.save(other='xxx')
###############################################
# serializer.py
class Ser(serializer.Serializer):
...
def create(self,validated_data):
other = validated_data.pop('other')
...
【4】source参数
source
参数用于指定要序列化的字段的源。通常,它用于在序列化器中访问模型实例的属性或方法,并将其值包含在序列化后的数据中。- 当你需要访问模型实例的属性或方法,并将其值序列化到响应中时,可以使用
source
参数来指定该属性或方法的名称。这个参数告诉序列化器从哪里获取数据以进行序列化。
【4.1】source参数跨表查询
# models.py
class Books(models.Model):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
# 如果是外键字段 # 不使用source参数指定将会返回整个publish模型实例
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
###############################################
# serializer.py
class Ser(serializers.Serializer):
...
'''可以指定返回外键字段对象所对应的属性值'''
publish_name = serializers.CharField(source='publish.name')
publish_addr = serializers.CharField(source='publish.addr')
'''更改字段序列化时显示的名称'''
book_name = serializers.CharField(source='title') # book_name的值就是模型表中的title字段
【4.2】source参数指定执行模型表中的方法
'''指定执行模型表中的方法'''
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
def name2desc(self):
'''执行一些代码'''
return self.name + self.desc
############################################
# ser.py
class TaskSer(serializers.Serializer):
# 通过source参数指定模型表中方法 # 传对应的函数名即可
name_desc = serializers.CharField(source='name2desc')
【4.3】source参数指定显示模型表中字段名称
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
############################################
# ser.py
class TaskSer(serializers.Serializer):
# task_name将作为序列化返回给前端的json数据中展示name字段的键
task_name = serializers.CharField(source='name')
【4.4】总结
# ser.py
from rest_framework import serializers
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
# 可以使用source参数自定义显示给前端的json数据中的键
task_name = serializers.CharField(source='name')
# 模型表中的方法也可以使用同名的字段名进行调用
name2desc = serializers.CharField()
# 也可以使用source参数执行模型表中的方法
name_desc = serializers.CharField(source='name2desc')
# 直接使用外键字段将回返回当前模型表对象对应的外键对象
user = serializers.CharField()
# 可以使用source参数进行跨表查询
user_name = serializers.CharField(source='user.username')
###############################################
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ForeignKey(to='User', on_delete=models.CASCADE, default=1)
def name2desc(self):
'''执行一些代码'''
return self.name + self.desc
class User(models.Model):
username = models.CharField(max_length=32, default='aaa')
【5】使用 SerializerMethodField 定制字段
# 基本语法
字段 = serializers.SerializerMethodField()
def get_字段(self,obj):
'''此处的obj就是当前正在序列化的模型表对象'''
return ...
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 可以使用source参数进行跨表查询
user_name = serializers.CharField(source='user.username')
xxx = serializers.SerializerMethodField()
def get_xxx(self, obj):
'''
此处的self为序列化类的对象,应当返回模型对象中的数据
:param obj: 当前模型表对象
:return: 想要展示的值
'''
return obj.desc + '这是描述哟'
【6】使用 子序列化 定制字段
- 使用子序列化以实现序列化类的复用
class UserSer(serializers.Serializer):
'''user表的序列化类'''
username = serializers.CharField()
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 使用子序列化以实现序列化类的复用
# 如果字段名与模型表中的外键字段一致 # 当调用子序列化时,将会自动将该外键对象传入子序列化类中
user = UserSer()
# 可以使用source参数指定该字段对应的外键字段 # 那么子序列化类将会自动识别外键对象
user_obj = UserSer(source='user')
- 如果既不使用与外键字段同名,且不使用source参数指定,将会报错
【7】ListField和DictField
ListField
用于序列化和反序列化列表类型的数据。- 当你序列化数据时,
ListField
会将列表中的每个元素序列化为相应的格式,例如 JSON 数组 - 当你反序列化时,
ListField
字段将可以接受数组类型的数据
- 当你序列化数据时,
DictField
用于序列化和反序列化字典类型的数据。- 当你序列化数据时,
DictField
会将字典中的键值对序列化为相应的格式,例如 JSON 对象。 - 当你反序列化时,
DictField
字段将可以接受字典类型的数据
- 当你序列化数据时,
【7.1】序列化使用场景
- 在模型表中,定义某些方法,返回列表或字典格式
- 【注】对于多对多字段,不能直接使用
ListField
来进行序列化,可以使用SerializerMethodField
定义方法- 在模型表中定义方法返回列表
- 使用子序列化并传递
many=True
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ForeignKey(to='User', on_delete=models.CASCADE, default=1)
def num_list(self):
return [1, 2, 3]
def info_dic(self):
return {'id': 1, 'username': self.user.username}
##################################################
# ser.py
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 所有的模型表中的字段类型都可以使用CharField强转 # 但是此时该字段就只是字符串了
l_str = serializers.CharField(source='num_list')
# 使用source参数指定
l = serializers.ListField(source='num_list')
# 使用同名的字段
num_list = serializers.ListField()
info_dic = serializers.DictField()
dic = serializers.DictField(source='info_dic')
- 多对多外键字段,序列化演示
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# # 多对多外键字段
# user = serializers.ListField() # 将会报错
# 使用 SerializerMethodField
user_list = serializers.SerializerMethodField()
def get_user_list(self, obj):
return [user.username for user in obj.user.all()]
# 使用子序列化
user_list2 = UserSer(source='user', many=True)
【7.2】反序列化使用场景
- 常用于多对多字段
# models.py
class Task(models.Model):
name = models.CharField(max_length=32)
desc = models.CharField(max_length=32)
user = models.ManyToManyField(to='User')
class User(models.Model):
username = models.CharField(max_length=32, default='aaa')
#################################################
# ser.py
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 使用ListField接收列表数据 # write_only表示该字段只进行反序列化 # 只用来接收前端的数据并保存
user = serializers.ListField(write_only=True)
# user = serializers.CharField(write_only=True) # "Not a valid string." # 将会报错
# 使用DictField接收字典形式数据
task_info = serializers.DictField(write_only=True)
def create(self, validated_data):
# 使用post创建对象时,必须重写create方法
# 否则会报错
print(validated_data)
# {'name': 'task3', 'desc': 'task3-desc', 'user': [1, 2], 'task_info': {'time': 'xxxx', 'style': 'aaaa'}}
return validated_data # 正常需要返回创建号的对象 # 此处将会返回序列化类序列化的数据
#################################################
# views.py
class TaskView(APIView):
def get(self, request):
ser = TaskSer(Task.objects.all(), many=True)
return Response(ser.data)
def post(self, request):
ser = TaskSer(data=request.data)
# 反序列化校验
ser.is_valid(raise_exception=True)
# 调用create方法
ser.save()
return Response(ser.data if ser.is_valid() else ser.errors)
【8】read_only
和write_only
- 当我们希望某些字段只执行序列化,也就是返回给前端时,可以使用
read_only
- 当我们希望某些字段只执行反序列化,也就是要求前端传值交由我们保存,可以使用
write_only
- 默认不填代表着,该字段既做序列化又做反序列化
class TaskSer(serializers.Serializer):
# 模型表中有的字段将会一一对应
name = serializers.CharField()
desc = serializers.CharField()
# 只做序列化,将用户信息展示给前端
user = UserSer(many=True, read_only=True)
# 只做反序列化,需要用户传值
user_l = serializers.ListField(write_only=True)
task_info = serializers.DictField(write_only=True)
【补充】其他知识
【1】字段参数validators
- 在字段中添加validators选项参数,可以补充验证行为,如
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
class BookInfoSerializer(serializers.Serializer):
"""图书数据序列化器"""
title = serializers.CharField(label='名称', max_length=20, validators=[about_django])
pub_date = serializers.DateField(label='发布日期', required=False)
read = serializers.IntegerField(label='阅读量', required=False)
comment = serializers.IntegerField(label='评论量', required=False)
image = serializers.ImageField(label='图片', required=False)
【2】反序列化校验的三层校验
- 执行字段内部的校验
- max_length / min_length 等
- validators :是一个列表,可以传多个校验函数,将会依次执行列表中的所有校验函数
- 执行局部钩子:
validate_字段
- 执行全局钩子:
validate
【3】无法直接使用source参数反向查询
- 直接使用
source
参数是无法实现复杂的反向查询的。source
参数通常用于简单的字段访问,例如直接从模型实例的属性中获取数据。 - 如果需要进行复杂的反向查询,例如从关联模型中获取相关对象列表,需要使用
SerializerMethodField
并定义一个方法来实现
【9】模型类序列化类(ModelSerializer)
- DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。
- ModelSerializer与常规的Serializer相同,但提供了:
- 基于模型类自动生成一系列字段
- 基于模型类自动为Serializer生成validators,比如unique_together
- 包含默认的create()和update()的实现
from rest_framework.serializers import ModelSerializer
class XXX(ModelSerializer):
# 不在模型表中的字段
字段 = serializers.CharField()
class Meta:
# 如果是模型表中的字段 # 可以在Meta中的fields参数中指定即可
model=对应的表
# 模型表中的字段将于序列化类的字段类型一一对应
fields = 需要使用的字段
#### 【注】此处的fields需要注意,不仅仅要填写在模型表中的字段,还需要填写额外的字段 ####
extra_kwargs = {
'字段':{'额外添加的参数'}
}
- 【注】
fields
是所有使用到的字段
from .models import Task
class TaskSerV2(serializers.ModelSerializer):
# 只做反序列化,需要用户传值
user_l = serializers.ListField(write_only=True)
task_info = serializers.DictField(write_only=True)
aaa = serializers.CharField(source='name')
class Meta:
model = Task
fields = '__all__' # __all__ 表示模型表中的所有字段
# fields = ['id','name','aaa','desc'] # 相当于
extra_kwargs = {
'name': {'max_length': 10}
}
'''如果所需字段就是表中需要的,就不需要重写create方法了'''
def create(self, validated_data):
# 由于Task表中并不需要下面两个字段,所以去除掉
validated_data.pop('user_l')
validated_data.pop('task_info')
# 调用ModelSerializer中的create方法
task = super().create(validated_data)
return task