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】序列化对象的常用参数
  1. instance:要序列化的模型实例。如果需要对现有对象进行序列化,则传递该实例。
  2. data:要反序列化的数据。如果需要从数据中创建对象,则传递该数据。通常用于创建或更新对象。
  3. context:上下文数据,可以在序列化器的各个方法中使用。通常用于在序列化器之间传递额外的信息。
  4. many:指定是否序列化多个对象。默认为 False。如果设置为 True,则可以序列化多个对象的查询集。
  5. 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')

image-20240422153057758

【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 + '这是描述哟'

image-20240422155215255

【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')

image-20240422160909664

  • 如果既不使用与外键字段同名,且不使用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')

image-20240422162856445

  • 多对多外键字段,序列化演示
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)

image-20240422165811807

【8】read_onlywrite_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)

image-20240422170618201

image-20240422170624778

【补充】其他知识

【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】反序列化校验的三层校验

  1. 执行字段内部的校验
    • max_length / min_length 等
    • validators :是一个列表,可以传多个校验函数,将会依次执行列表中的所有校验函数
  2. 执行局部钩子:validate_字段
  3. 执行全局钩子: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

posted @   Lea4ning  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示