Tortoise-orm信号实现及使用场景源码详解
目录
场景源码看看在模型save的时候,都干了什么?自己实现一个信号总结场景
在使用Tortoise操作数据库的时候发现,通过对操作数据库模型加以装饰器,如@pre_save(Model)
,可以实现对这个模型在savue
时,自动调用被装饰的方法,从而实现对模型的一些操作。
(资料图片)
在此先从官方文档入手,看一下官方的对于模型信号的Example
# -*- coding: utf-8 -*- """ This example demonstrates model signals usage """ from typing import List, Optional, Type from tortoise import BaseDBAsyncClient, Tortoise, fields, run_async from tortoise.models import Model from tortoise.signals import post_delete, post_save, pre_delete, pre_save class Signal(Model): id = fields.IntField(pk=True) name = fields.TextField() class Meta: table = "signal" def __str__(self): return self.name @pre_save(Signal) async def signal_pre_save( sender: "Type[Signal]", instance: Signal, using_db, update_fields ) -> None: print("signal_pre_save", sender, instance, using_db, update_fields) @post_save(Signal) async def signal_post_save( sender: "Type[Signal]", instance: Signal, created: bool, using_db: "Optional[BaseDBAsyncClient]", update_fields: List[str], ) -> None: print("post_save", sender, instance, using_db, created, update_fields) @pre_delete(Signal) async def signal_pre_delete( sender: "Type[Signal]", instance: Signal, using_db: "Optional[BaseDBAsyncClient]" ) -> None: print("pre_delete", sender, instance, using_db) @post_delete(Signal) async def signal_post_delete( sender: "Type[Signal]", instance: Signal, using_db: "Optional[BaseDBAsyncClient]" ) -> None: print("post_delete", sender, instance, using_db) async def run(): await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]}) await Tortoise.generate_schemas() # pre_save,post_save will be send signal = await Signal.create(name="Signal") signal.name = "Signal_Save" # pre_save,post_save will be send await signal.save(update_fields=["name"]) # pre_delete,post_delete will be send await signal.delete() if __name__ == "__main__": run_async(run())
以上代码可直接复制后运行,运行后的结果:
signal_pre_save
Signal None
post_saveSignal True None
signal_pre_saveSignal_Save ["name"]
post_saveSignal_Save False ["name"]
pre_deleteSignal_Save
post_deleteSignal_Save
可以发现,对模型进行保存和删除时候,都会调用对应的信号方法。
源码
从导包可以得知,tortoise的所有信号方法都在tortoise.signals
中。
from enum import Enum from typing import Callable Signals = Enum("Signals", ["pre_save", "post_save", "pre_delete", "post_delete"]) def post_save(*senders) -> Callable: """ Register given models post_save signal. :param senders: Model class """ def decorator(f): for sender in senders: sender.register_listener(Signals.post_save, f) return f return decorator def pre_save(*senders) -> Callable: ... def pre_delete(*senders) -> Callable: ... def post_delete(*senders) -> Callable: ...
其内部实现的四个信号方法分别是模型的保存后,保存前,删除前,删除后。
其内部装饰器代码也十分简单,就是对装饰器中的参数(也就是模型),注册一个监听者,而这个监听者,其实就是被装饰的方法。
如上面的官方示例中:
# 给模型Signal注册一个监听者,它是方法signal_pre_save @pre_save(Signal) async def signal_pre_save( sender: "Type[Signal]", instance: Signal, using_db, update_fields ) -> None: print("signal_pre_save", sender, instance, using_db, update_fields)
而到了Model类中,自然就有一个register_listener方法,定睛一看,上面示例Signal中并没有register_listener方法,所以自然就想到了,这个方法必定在父类Model中。
class Model: ... @classmethod def register_listener(cls, signal: Signals, listener: Callable): ... if not callable(listener): raise ConfigurationError("Signal listener must be callable!") # 检测是否已经注册过 cls_listeners = cls._listeners.get(signal).setdefault(cls, []) # type:ignore if listener not in cls_listeners: # 注册监听者 cls_listeners.append(listener)
接下来注册后,这个listeners就会一直跟着这个Signal类。只需要在需要操作关键代码的地方,进行调用即可。
看看在模型save的时候,都干了什么?
async def save( self, using_db: Optional[BaseDBAsyncClient] = None, update_fields: Optional[Iterable[str]] = None, force_create: bool = False, force_update: bool = False, ) -> None: ... # 执行保存前的信号 await self._pre_save(db, update_fields) if force_create: await executor.execute_insert(self) created = True elif force_update: rows = await executor.execute_update(self, update_fields) if rows == 0: raise IntegrityError(f"Can"t update object that doesn"t exist. PK: {self.pk}") created = False else: if self._saved_in_db or update_fields: if self.pk is None: await executor.execute_insert(self) created = True else: await executor.execute_update(self, update_fields) created = False else: # TODO: Do a merge/upsert operation here instead. Let the executor determine an optimal strategy for each DB engine. await executor.execute_insert(self) created = True self._saved_in_db = True # 执行保存后的信号 await self._post_save(db, created, update_fields)
抛开其他代码,可以看到,在模型save的时候,其实是先执行保存前的信号,然后执行保存后的信号。
自己实现一个信号
有了以上的经验,可以自己实现一个信号,比如我打算做个数据处理器的类,我想在这个处理器工作中,监听处理前/后的信号。
# -*- coding: utf-8 -*- from enum import Enum from typing import Callable, Dict # 声明枚举信号量 Signals = Enum("Signals", ["before_process", "after_process"]) # 处理前的装饰器 def before_process(*senders): def decorator(f): for sender in senders: sender.register_listener(Signals.before_process, f) return f return decorator # 处理后的装饰器 def after_process(*senders): def decorator(f): for sender in senders: sender.register_listener(Signals.after_process, f) return f return decorator class Model(object): _listeners: Dict = { Signals.before_process: {}, Signals.after_process: {} } @classmethod def register_listener(cls, signal: Signals, listener: Callable): """注册监听者""" # 判断是否已经存在监听者 cls_listeners = cls._listeners.get(signal).setdefault(cls, []) if listener not in cls_listeners: # 如果不存在,则添加监听者 cls_listeners.append(listener) def _before_process(self): # 取出before_process监听者 cls_listeners = self._listeners.get(Signals.before_process, {}).get(self.__class__, []) for listener in cls_listeners: # 调用监听者 listener(self.__class__, self) def _after_process(self): # 取出after_process监听者 cls_listeners = self._listeners.get(Signals.after_process, {}).get(self.__class__, []) for listener in cls_listeners: # 调用监听者 listener(self.__class__, self) class SignalModel(Model): def process(self): """真正的调用端""" self._before_process() print("Processing") self._after_process() # 注册before_process信号 @before_process(SignalModel) def before_process_listener(*args, **kwargs): print("before_process_listener1", args, kwargs) # 注册before_process信号 @before_process(SignalModel) def before_process_listener(*args, **kwargs): print("before_process_listener2", args, kwargs) # 注册after_process信号 @after_process(SignalModel) def before_process_listener(*args, **kwargs): print("after_process_listener", args, kwargs) if __name__ == "__main__": sm = SignalModel() sm.process()
输出结果:
before_process_listener1 (
, <__main__.SignalModel object at 0x7ff700116e50>) {}
before_process_listener2 (, <__main__.SignalModel object at 0x7ff700116e50>) {}
Processing
after_process_listener (, <__main__.SignalModel object at 0x7ff700116e50>) {}
总结
笔者通过对`tortoise-orm`源码的学习,抽丝剥茧,提取了信号实现的方式。其核心就是通过一个字典存储调用方自定义的process方法,然后分别在真正的调用端的前/后触发这些自定义方法即可。
以上就是Tortoise-orm信号实现及使用场景源码详解的详细内容,更多关于Tortoise orm信号场景的资料请关注脚本之家其它相关文章!
标签:
推荐
- Tortoise-orm信号实现及使用场景源码详解
- 就医体验差、等候时间长?多部门联合开展活动改善患者就医体验_当前快讯
- 世界快资讯:《烈焰皇朝》倒计时1天&预下载安装开启!
- 世界今热点:心态的名言古诗_心态的名言
- C919商业首航,关注军工ETF投资机会
- 男女足同时夺冠,拜仁在慕尼黑市政厅阳台庆祝冠军
- 焦点速递!张子强_张子
- 全球热讯:江阴富商之子 做出3只独角兽
- 天天百事通!治理变“智理” 制造变“智造”
- 减肥水果酵素的做法和配方_减肥水果酵素的做法_世界热议
- 环球快看:在运动中感受生态魅力!600多名“铁人”在东莞同沙生态公园竞速
- 中超第二轮赛程(中超第二轮)
- 里皮执教中国队年薪多少 中国男足主教练里皮年薪是多少 动态
- 腾讯《街头篮球》手游国服7月25日关服|每日快播
- 机关算尽太聪明反误了卿卿性命出自哪里(机关算尽太聪明反误了卿卿性命) 即时焦点
- 昆明市校(园)长论坛聚焦生涯教育助力学生成长-全球观焦点
- 华润通是什么东西(华润通是什么)_世界热点评
- 世界热讯:大概率涨钱 国内油价5月30日晚开启调整窗口
- 英特尔Arc入局显卡市场
- 每日看点!“绿军”0.1秒反绝杀,NBA史上首次“让三追四”要来了?
- 每日热点:易拉宝印刷分辨率多少合适(易拉宝分辨率多少合适)
- 无锡二日游攻略自由行 无锡旅游攻略二日游
- 优质资源普惠共享!这堂课将被送进徐汇8万余名学生的课堂中|全球热门
- 华夏理财董事长苑志宏:国内银行理财ESG产品规模未来市场空间可能超过9万亿元
- 重点聚焦!第三金!陈梦/王艺迪女双夺冠
- 天天观察:邵阳:“暖心战友”为买毒,不惜身陷囹圄
- 铜川市气象台继续发布大雾黄色预警【Ⅲ级/较重】【2023-05-28】 焦点简讯
- 记者调查丨乡亲们注意,忽悠团又来啦!
- 崇信县成功入选第四批国家农村产业融合发展示范园创建名单
- 湿地植物净化水质的原理_湿地植物
- 湖人不会两年6750万提前续约拉塞尔
- 全球观察:老将迈入人生新赛场,上海104名优秀运动员退役,奥运冠军钟天使转型当教练
- 前英国央行的鹰派利率制定者警告称,利率需要飙升至6%才能遏制通胀,央行认为这一水平对家庭和企业来说是痛苦的
- 天天关注:iv是什么(iv在医学上是什么意思)
- 黑鱼的功效与作用(黑鱼的功效与作用禁忌)
- 每日头条!基辛格:让乌克兰加入北约的想法严重错误,导致了俄乌冲突
- 孙颖莎4-1艰难晋级决赛!后三局太焦灼,莎莎认可早田希娜实力!
- 环球今亮点!双机热备如何部署_双机热备
- 全球热消息:ppt砸金蛋的全制作过程_ppt砸金蛋怎么制作
- 科技赋能助夏管 智慧农业保丰收
- 前沿资讯!糯米粉的做法大全点心_2种做法分享
- 中国上市公司协会会长宋志平:上市公司要重视对股东的价值创造
- 世界播报:九月九重阳节那天是干什么的 九月九重阳节什么含义
- 【天天播资讯】2023高考问答|高考志愿填报一般需要考虑哪些因素?
- 自考成绩觉得不对可以再查吗?你需要自考成绩复核!
- 怎么样共享打印机到另一台电脑_怎么链接共享打印机 每日快讯
- 武汉路站(关于武汉路站介绍)|当前观察
- 何须生入玉门关_何须 当前时讯
- 全球头条:市区随迁子女及回扬升学学生注意啦 26日起进行网上信息登记
- 富豪官宣离婚!140亿元市值股票全部归女方 男方从资本市场上“净身出户”
- 环球新消息丨【优化营商环境】原阳法院开展涉执企业回访活动
- 多城新房销售量下滑 6月是房企年中冲刺业绩的关键
- 当前快讯:端午节说说祝福语简单_端午节说说
- 执输行头惨过败家翻译 执输行头惨过败家-今日看点
- 砂子塘万境水岸小学:心理健康日,丰富活动教会学生释放压力
- 天天微速讯:渔家傲原文及翻译注释李清照_渔家傲原文及翻译
- 世界快资讯:C级轿跑和E级轿跑替代品来袭 奔驰CLE敞篷版谍照曝光
- 给足诚意乘胜追击 比亚迪汉DM-i/DM-p双车试驾 微头条
- 宁波打造新能源汽车城,预计2025年新能源汽车产量占全市汽车总产量超50%_当前报道
- 天天动态:道中华丨霍巍:考古实证西藏历史是各民族共同书写的(下)
- 聚焦:5月26日期货软件走势图综述:液化石油气期货主力跌3.06%
- 离队第一人?曝詹姆斯与他位置冲突,湖人阵容变动,不再需要他了
- 中期协副会长王颖:期现结合及场外业务在钢铁业风险管理中作用凸显_全球百事通
- 验证“复兴号”高铁平稳性,16位外国媒体记者在时速 305公里列车上比赛竖硬币 热门
- 如何修复分离的拉链|世界今日讯
- 牛奶到底是早上喝好还是晚上好?喝牛奶几大误区,看完长知识了!_天天简讯
- 当前短讯!江汉区晶晶幼儿园开展清廉文化主题作品展
- 天天微速讯:gba金手指怎么用_gba金手指用的方法
- 环球热讯:“外卖小哥”:风一样的速度 火一样的热情
- 广东银保监局裴光:支持刚性和改善性住房金融需求 做好“保交楼”金融服务
- 长城到底举报了个啥? 每日资讯
- 北京今年新能源指标“分数线”为60分|世界焦点
- “保命药”价格飞涨一药难求?药企回应来了
- 贵州国台酒业发布声明:在抖音、快手等电商平台出现假冒产品
- 她一封神,85 花全被打脸-全球信息
- 当前动态:【消费提示】新疆维吾尔自治区市场监督管理局关于蜂蜜安全的消费提示
- 联合国教科文组织新指定18个世界地质公园
- 今日要闻!东安人陈云龙身残志坚,带领残疾人解决就业创业难题——他带着残疾人当网红
- 焦点速递!常泰长江大桥主塔上塔柱钢塔全面施工
- 30秒2次惊艳表演!19岁国安小将在亚冠踢嗨了:外脚背+插花脚传球 环球资讯
- 新正与邪_关于新正与邪简介
- 因长得酷似日本鬼子,4年被邀请出演6千多次,成为鬼子专业户
- 【透视】英媒专栏作家纽约行:“美国梦”已成为一场噩梦!|全球最资讯
- 潍坊文昌中学是私立学校吗(潍坊文昌中学会转公立吗)
- 全球快播:头发出油严重怎么治_头发出油厉害怎么调理
- 世界资讯:力克强敌晋级世乒赛四强 陈梦/王艺迪通过实战增进信任
- 作业帮怎么扫码出整本答案 作业帮可以扫码搜答案吗
- 当前消息!爱奇艺电视果投屏器 5K 发布:支持 4K 超高清投屏,198 元
- 【20230526早评】下跌250点,谁才是250?_世界动态
- 世界今日报丨义务帮人遛狗时摔伤 狗主人是否要赔偿
- 从事反电信网络诈骗工作,却帮不法分子查询银行卡信息!获刑
- 衡水市新增346家国家科技型中小企业-全球最资讯
- 案外人执行异议之诉指的是什么?执行异议在什么时候提?
- 记者调查| 建好的房子无法通过验收? 九江嘉圆•悦湖居延期交房引质疑-当前简讯
- 一直喝普洱茶可以减肥吗 天天热门
- 肿瘤分期的意义 焦点简讯
- 郑商所棉花期货主力合约跌3%|世界聚看点
- 东威科技涨9.50%,浙商证券一个月前给出“买入”评级
- 南大光电于苏州参设材料公司 含细胞技术研发和应用业务
- 国际乒联第21周排名:樊振东积分锐减,第1位置或不保,莎莎三项第1
X 关闭
行业规章
X 关闭