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 四. 小结 示例代码托管在: ...
随机推荐
- Apache DolphinScheduler 项目笔记 — 1. 问题定位和排查问题
导语: Yuanhao Ji 同学是<暑期 2021 - API 模块 UT 测试问题解决>项目负责人,本文记录了他是如何排查和定位问题. 1. 背景 问题:在部分 UT 中使用 Powe ...
- 原生js也可以自定义组件
Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们. 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元 ...
- CF383C Propagating tree (线段树,欧拉序)
\(tag\)没开够\(WA\)了一发... 求出\(dfs\)序,然后按深度分类更新与查询. #include <iostream> #include <cstdio> #i ...
- LuoguP1725 琪露诺 (动态规划)
\(单调队列\) 或 \(堆\) 优化 #include <iostream> #include <cstdio> #include <cstring> #incl ...
- zabbix 报表动作日志 报错”503“
- Markdown使用指南
1. Markdown是什么? Markdown是一种轻量级标记语言,它以纯文本形式(易读.易写.易更改)编写文档,并最终以HTML格式发布. Markdown也可以理解为将以MARKDOWN语法编写 ...
- 通过宏封装实现std::format编译期检查参数数量是否一致
背景 std::format在传参数量少于格式串所需参数数量时,会抛出异常.而在大部分的应用场景下,参数数量不一致提供编译报错更加合适,可以促进我们更早发现问题并进行改正. 最终效果 // 测试输出接 ...
- 并发编程二、CPU多级缓存架构与MESI协议的诞生
前言: 文章内容:线程与进程.线程生命周期.线程中断.线程常见问题总结 本文章内容来源于笔者学习笔记,内容可能与相关书籍内容重合 偏向于知识核心总结,非零基础学习文章,可用于知识的体系建立,核心内容 ...
- 第五篇:vue.js起步
<div id="vue_det"> //使改动全部在指定的 div 内,div 外部不受影响 <h1>site : {{site}}</h1> ...
- KingbaseES R6 集群创建流复制只读副本库案例
一.环境概述 [kingbase@node2 bin]$ ./ksql -U system test ksql (V8.0) Type "help" for help. test= ...