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 四. 小结 示例代码托管在: ...
随机推荐
- SpringBoot(一、快速入门)
1.SpringBoot简介 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置, ...
- 我和Apache DolphinScheduler的缘分
关于 DolphinScheduler社区 Apache DolphinScheduler(incubator) 于17年在易观数科立项,19年3月开源, 19 年8月进入Apache 孵化器,社区发 ...
- 乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 官方扩展集锦(Microsoft.Extensions on Nuget)
什么是Microsoft.Extensions .NET Platform Extensions是一套.Net官方的API集合,提供了一些常用的编程模式和实用工具,例如依赖项注入.日志记录.缓存.Ho ...
- 图床搭建|chrome插件|操作简单不要钱
为什么需要一个图床 为了写博客 图床:专门用来存放图片,同时允许你把图片对外连接的网上空间. 用markdown格式写作,插入图片需要图片的url地址,然后博客的背景图也是要用url地址. 有段时间, ...
- format添加未知个参数方法
一个python巧妙技巧,分享给大家 我的需求是将一个dict的键都format输出,用到了*对字典解包 data = {a: 1, b: 2...} msg = f"{'{} '*len( ...
- 【多服务场景化解决方案】智能家居(UrbanHome)
介绍 UrbanHome是一款提供房屋维修服务的移动应用.如有维修需求,用户可通过该应用联系所在城市的管道工,电工,保洁,漆匠,木匠,修理工等,或是搜寻导航附近的维修商店. 通过构建UrbanHo ...
- 当 SQL DELETE 邂逅 Table aliases,会擦出怎样的火花
开心一刻 晚上,女儿眼噙泪水躺在床上 女儿:你口口声声说爱我,说陪我,却天天想着骗我零花钱,你是我亲爹吗? 我:你想知道真相 女儿:想! 我:那你先给爸爸两百块钱! 环境准备 MySQL 不同版本 利 ...
- 跟羽夏学 Ghidra ——数据
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- 【debug技巧】jstat:虚拟机统计信息监视器
我们在日常开发时,难免会遇到一些没有内存泄漏等问题.有时,我们无法下载arthas等开源的诊断工具.这时候,我们就可以借助JDK自带的一些诊断工具. 首先我们可以使用jstat查看gc信息 字段含义 ...
- 配置Kubelet的垃圾回收
文章转载自:https://www.kuboard.cn/learning/k8s-advanced/gc.html Kubelet的垃圾回收功能可以清理不再使用的容器和镜像,kubelet对容器进行 ...