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 四. 小结 示例代码托管在: ...
随机推荐
- The Data Way Vol.1|风口下的开源:如何看待开源与商业的关系?
关于「The Data Way」 「The Data Way」是由 SphereEx 公司出品的一档播客节目.这里有开源.数据.技术的故事,同时我们关注开发者的工作日常,也讨论开发者的生活日常:我们聚 ...
- 如何在win下安装dlib的whl文件(Anaconda方式)
问题描述 由于作业需要用到dlib的人脸检测函数,所以尝试安装了一下dlib.顺便贴上dlib的下载网址dlib下载. 但当我直接输入pip install dlib-19.7.0-cp36-cp36 ...
- MySQL(一)——查看密码与修改
查看数据库密码,策略与修改 查看数据库密码: RPM安装: # cat /var/log/mysqld.log | grep password 源码安装: # cat /usr/local/mysql ...
- Go语言Tips
时间日期格式化 time.Now().Format("2006-01-02") 原生DefaultServeMux支持restful路由 ref: https://towardsd ...
- Java精进-20分钟学会mybatis使用
文字分享 希望现在的你无论有明确具体的目标还是没有,都能重视自己的需求和目标,并且常常回顾,或许可以找一个你习惯的方式写出来,挂在哪里,电脑或日记本都好.当你疲惫或迷茫的时候拿出来看一下,这在情怀领域 ...
- Jetpack架构组件学习(4)——APP Startup库的使用
最近在研究APP的启动优化,也是发现了Jetpack中的App Startup库,可以进行SDK的初始化操作,于是便是学习了,特此记录 原文:Jetpack架构组件学习(4)--App Startup ...
- JavaScript 闭包 —— { }
闭包是什么 闭包是一个[函数](一个作用域可以访问另一个函数的局部变量) 闭包的作用 延伸变量的作用域 function f() { let num = 21; return function () ...
- UE4自动打包工具编写
在UE的开发中,有些项目需要针对不同版本出不同的包,并有一个对应的GUI界面,供大家使用. 1.插件编写 先使用UE4自己的插件模板创建插件,做成插件形式 然后注册Slate UI,编写打开逻辑.并在 ...
- 海康摄像机使用GB28181接入SRS服务器的搭建步骤---源码安装的方式
下载代码 地址:https://github.com/ossrs/srs-gb28181 https://github.com/ossrs/srs-gb28181.git 注意:使用的是含有gb281 ...
- Solutions:Elastic workplace 搜索:随时随地搜索所有内容 (二)