30.Serializers模块源码解析
field类:
序列化基类的基类
BaseSerializer:
继承field
派生ListSerializer序列化类
Serializer:
继承SerializerMetaClass
继承BaseSerializer
ModelSerializer:
继承Serializer
HyperlinkedModelSerializer:
继承ModelSerializer
- 生成序列化对象serializer([instance],data=request.data)
- 数据验证:is_valid()
- 生成:validated_data
- 调用save方法
- 根据是否有instance,分别调用create或者update
- 返回serializer.data
除了ListSerializer类不太一样
BaseSerializer和Serializer、ModelSerializer三个类的is_valid和save则是完全一样
create和update只有ModelSerializer有直接实现
HyperLinkedModelSerializer是继承ModelSerializer基础上增加了url字段和嵌套
# BaseSerializer中的create
def create(self, validated_data):
raise NotImplementedError('`create()` must be implemented.')
#在Baseserliazer序列化基类的时候,创建了create方法
# 但是并没有具体去实现他,只是抛出未实现的异常
'''
Serializer继承BaseSerializer,没有直接提供写好的create方法
所以我们继承Serializer实现序列化的时候,需要自己去写create
ModelSerializer源码中实现了create,可以直接使用
'''
# ModelSerializer源码中的create
def create(self, validated_data):
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model # 获取模型类
info = model_meta.get_field_info(ModelClass) # 获取字段信息赋值给info
many_to_many = {} #多对多空字典
for field_name, relation_info in info.relations.items(): # 遍历info中的字段信息
# 如果是多对多字段全部拿出去后面单独处理
if relation_info.to_many and (field_name in validated_data):
many_to_many[field_name] = validated_data.pop(field_name)
try:
# 生成模型的实例对象,赋值给instance
instance = ModelClass._default_manager.create(**validated_data)
except TypeError: # 异常捕获类型错误
tb = traceback.format_exc()
msg = (
'Got a `TypeError` when calling `%s.%s.create()`. '
'This may be because you have a writable field on the '
'serializer class that is not a valid argument to '
'`%s.%s.create()`. You may need to make the field '
'read-only, or override the %s.create() method to handle '
'this correctly.\nOriginal exception was:\n %s' %
(
ModelClass.__name__,
ModelClass._default_manager.name,
ModelClass.__name__,
ModelClass._default_manager.name,
self.__class__.__name__,
tb
)
)
raise TypeError(msg)
# Save many-to-many relationships after the instance is created.
if many_to_many: # 如果多对多有值字典有值
for field_name, value in many_to_many.items(): # 遍历多对多信息字典
field = getattr(instance, field_name) #通过getattr生成对应实例
field.set(value) #将value保存
return instance
'''
整体逻辑就是先将每个字段的值拿出来,先用非多对多字段的值创建模型实例
最后遍历多对多字段值保持到实例,最后返回
'''
'''
update同样也是在BaseSerializer中留下了对应方法的位置待后续实现
Serializer中没有做具体实现,直接继承Serializer类需要自己实现update
下面是ModelSerializer中实现的update源码
'''
def update(self, instance, validated_data):
raise_errors_on_nested_writes('update', self, validated_data)
info = model_meta.get_field_info(instance)
m2m_fields = []
for attr, value in validated_data.items(): #直接遍历所有已验证的数据,不区分多对多关系
# 如果是多对多字段,
if attr in info.relations and info.relations[attr].to_many:
m2m_fields.append((attr, value)) #追加到m2m
else:
setattr(instance, attr, value) # 更新字段数据
instance.save()
for attr, value in m2m_fields: #遍历m2m字段
field = getattr(instance, attr) # 更新数据
field.set(value)
return instance
'''
is_valid在BaseSerializer中有直接实现
'''
def is_valid(self, raise_exception=False):
assert hasattr(self, 'initial_data'), (
'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
if not hasattr(self, '_validated_data'): # 如果没有self._validated_data
try: #使用self.run_validation验证数据,如果没有问题赋值给self._validated_data
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = {} #如果有问题将_validated_data赋值为一个空字典
self._errors = exc.detail # errors信息
else:
self._errors = {}
if self._errors and raise_exception: # 如果有erroes信息和raise_exception设置为True
raise ValidationError(self.errors) #抛出ValidationError异常信息
return not bool(self._errors) #如果self._errors没有值就是一个空字典,bool就是False,not bool就是True
def save(self, **kwargs):
# 断言否包含error
assert hasattr(self, '_errors'), (
'You must call `.is_valid()` before calling `.save()`.'
)
# 断言 self.errors是否有错误信息
assert not self.errors, (
'You cannot call `.save()` on a serializer with invalid data.'
)
# 检查参数是否包含commit字段
assert 'commit' not in kwargs, (
"'commit' is not a valid keyword argument to the 'save()' method. "
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
"You can also pass additional keyword arguments to 'save()' if you "
"need to set extra attributes on the saved model instance. "
"For example: 'serializer.save(owner=request.user)'.'"
)
# 检查是否是使用self._data后调用.save()
assert not hasattr(self, '_data'), (
"You cannot call `.save()` after accessing `serializer.data`."
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
)
# 经过合格验证的数据,self.validated_data的每一项和kwargs的每一项
validated_data = {**self.validated_data, **kwargs}
# 通过判断instance是否有值来决定是更新操作还是创建操作
if self.instance is not None: #如果有值
# 调用update并且返回对应结果
self.instance = self.update(self.instance, validated_data)
# 断言更新后的结果来判断是否更新成功
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:#如果没有instance,就执行create方法
self.instance = self.create(validated_data)
#断言创建结果
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
return self.instance
'''
一般来说我们调用save是不传值
下方代码调用save的时候,传递了owner=self.request.user
owner模型类的一个字段
'''
def perfortm_create(self,serializer):
serializer.save(owner=self.request.user)
'''
save源码中的validated_data = {**self.validated_data, **kwargs}
除了self.validated_data以外,还有kwargs
所以validated_data是 self.validated_data验证过的数据 和 附加数据 kwargs组合起来的一个dict
'''
'''
一般来说 save方法是去找create或者update方法来做数据更新、创建
'''
class EmailSerializer(serializer.Serializer):
email = serializers.EmailField()
message = serializer.CharField()
def save(self):
email = self.validated_data['email'] #拿到经过验证的email字段
message = self.validated_data['message'] #拿到经过验证的message的字段
send_meail(from=email,message=message) #发送邮件
'''
上面的代码直接重写了save方法,覆盖源码的save,不再具有更新、创建的功能
is_valid\save\create\update是反序列化的最重要的几个逻辑方法
在序列化中,我们可以完完全全重写几个方法源码的逻辑根据我们的业务逻辑来定制我们的场景
'''
30.Serializers模块源码解析的更多相关文章
- gorm的日志模块源码解析
gorm的日志模块源码解析 如何让gorm的日志按照我的格式进行输出 这个问题是<如何为gorm日志加traceId>之后,一个群里的朋友问我的.如何让gorm的sql日志不打印到控制台, ...
- 「从零单排canal 06」 instance模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- php 日志模块源码解析
php日志模块设计 Monolog 是PHP的一个日志类库解析 整体介绍:monolog日志模块遵循 PSR3 的接口规范.主要有日志格式类接口(格式化日志信息),处理类接口(写日志的驱动,通过扩展写 ...
- C#软件授权、注册、加密、解密模块源码解析并制作注册机生成license
最近做了一个绿色免安装软件,领导临时要求加个注册机制,不能让现场工程师随意复制.事出突然,只能在现场开发(离开现场软件就不受我们控了).花了不到两个小时实现了简单的注册机制,稍作整理. ...
- 「从零单排canal 05」 server模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- 「从零单排canal 07」 parser模块源码解析
基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...
- python Threading模块源码解析
查看源码: 这是一个线程控制的类,这个类可以被子类化(继承)在一定的条件限制下,这里有两种方式去明确活动:第一通过传入一个callable 对象也就是调用对象,一种是通过重写这个Thread类的run ...
- 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
[摘要] 集群管理模块cluster浅析 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 概述 cluster模块是node.js中用于实现和管理 ...
- 【nodejs原理&源码赏析(4)】深度剖析cluster模块源码与node.js多进程(上)
目录 一. 概述 二. 线程与进程 三. cluster模块源码解析 3.1 起步 3.2 入口 3.3 主进程模块master.js 3.4 子进程模块child.js 四. 小结 示例代码托管在: ...
随机推荐
- Map集合的遍历方式以及TreeMap集合保存自定义对象实现比较的Comparable和Comparator两种方式
Map集合的特点 1.Map集合中保存的都是键值对,键和值是一一对应的 2.一个映射不能包含重复的值 3.每个键最多只能映射到一个值上 Map接口和Collection接口的不同 Map是双列集合的根 ...
- WebGPU的计算着色器实现冒泡排序
大家好~本文使用WebGPU的计算着色器,实现了奇偶排序. 奇偶排序是冒泡排序的并行版本,在1996年由J Kornerup提出.它解除了每轮冒泡间的串行依赖以及每轮冒泡内部的串行依赖,使得冒泡操作可 ...
- BZOJ3262/Luogu3810 陌上花开 (三维偏序,CDQ)
一个下午的光阴之死,凶手是细节与手残. 致命的一枪:BIT存权值时: for(; x <= maxx; x += x&-x) t[x] += w; //for(; x <= n; ...
- 急如闪电快如风,彩虹女神跃长空,Go语言高性能Web框架Iris项目实战-初始化项目ep00
在Golang Web编程的世界里,君不言高性能则已,言高性能必称Iris.彩虹女神的名号响彻寰宇.名动江湖,单论一个快字,无人能出其右,就连以简洁轻量著称于世的Gin也难以望其项背,只见彩虹女神Ir ...
- mongo数据同步的三种方案
(一)直接复制data目录(需要停止源和目标的mongo服务)1.针对目标mongo服务已经存在,并正在运行的(mongo2-->mongo).执行步骤:(1).停止源/目标服务器的mongo服 ...
- Web 布局设计(一):固定侧边栏
前言 闲着无事,做一些实战练习,今天实现一个如标题所示的布局设计.通过此次布局设计,我希望掌握position属性值 fixed.absolute.relative.width和height属性值 i ...
- 手把手教你搭建JAVA分布式爬虫
在工作中,我们经常需要去获取一些数据,但是这些数据可能需要从第三方平台才可以获取到.这个时候,爬虫系统就可以帮助我们来完成这些事情. 提到爬虫系统,很多人都会想到使用python.但实际上,语言只是一 ...
- 四 多例模式【Multition Pattern】 来自CBF4LIFE 的设计模式
出现在明朝,那三国期间的算不算,不算,各自称帝,各有各的地盘,国号不同.大家还记得那首诗<石灰吟>吗?作者是谁?于谦,他是被谁杀死的?明英宗朱祁镇,对,就是那个在土木堡之变中被瓦刺俘虏的皇 ...
- Docker 搭建 Nexus3 私服 | 基本操作
1 Docker 安装 Nexus3 1.1 创建目录 在硬盘上创建 Nexus3 的主目录: mkdir -p /Users/yygnb/dockerMe/nexus3 为该目录添加权限: chmo ...
- Session认证机制与JWT认证机制
一.什么是身份认证? 身份认证(Authentication)又称"身份验证"."鉴权",是指通过一定的手段,完成对用户身份的确认.日常生活中的身份认证随处可见 ...