最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Tortoise-orm信號(hào)實(shí)現(xiàn)及使用場(chǎng)景源碼詳解

 更新時(shí)間:2023年03月15日 14:44:35   作者:it_miclon  
這篇文章主要為大家介紹了Tortoise-orm信號(hào)實(shí)現(xiàn)及使用場(chǎng)景源碼詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

場(chǎng)景

在使用Tortoise操作數(shù)據(jù)庫(kù)的時(shí)候發(fā)現(xiàn),通過(guò)對(duì)操作數(shù)據(jù)庫(kù)模型加以裝飾器,如@pre_save(Model),可以實(shí)現(xiàn)對(duì)這個(gè)模型在savue時(shí),自動(dòng)調(diào)用被裝飾的方法,從而實(shí)現(xiàn)對(duì)模型的一些操作。

在此先從官方文檔入手,看一下官方的對(duì)于模型信號(hào)的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())

以上代碼可直接復(fù)制后運(yùn)行,運(yùn)行后的結(jié)果:

signal_pre_save <class '__main__.Signal'> Signal <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400> None
post_save <class '__main__.Signal'> Signal <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400> True None
signal_pre_save <class '__main__.Signal'> Signal_Save <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400> ['name']
post_save <class '__main__.Signal'> Signal_Save <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400> False ['name']
pre_delete <class '__main__.Signal'> Signal_Save <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400>
post_delete <class '__main__.Signal'> Signal_Save <tortoise.backends.sqlite.client.SqliteClient object at 0x7f8518319400>

可以發(fā)現(xiàn),對(duì)模型進(jìn)行保存和刪除時(shí)候,都會(huì)調(diào)用對(duì)應(yīng)的信號(hào)方法。

源碼

從導(dǎo)包可以得知,tortoise的所有信號(hào)方法都在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:
    ...

其內(nèi)部實(shí)現(xiàn)的四個(gè)信號(hào)方法分別是模型的保存后,保存前,刪除前,刪除后。

其內(nèi)部裝飾器代碼也十分簡(jiǎn)單,就是對(duì)裝飾器中的參數(shù)(也就是模型),注冊(cè)一個(gè)監(jiān)聽者,而這個(gè)監(jiān)聽者,其實(shí)就是被裝飾的方法。

如上面的官方示例中:

# 給模型Signal注冊(cè)一個(gè)監(jiān)聽者,它是方法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類中,自然就有一個(gè)register_listener方法,定睛一看,上面示例Signal中并沒有register_listener方法,所以自然就想到了,這個(gè)方法必定在父類Model中。

class Model:
    ...
    @classmethod
    def register_listener(cls, signal: Signals, listener: Callable):
        ...
        if not callable(listener):
            raise ConfigurationError("Signal listener must be callable!")
        # 檢測(cè)是否已經(jīng)注冊(cè)過(guò)
        cls_listeners = cls._listeners.get(signal).setdefault(cls, [])  # type:ignore
        if listener not in cls_listeners:
            # 注冊(cè)監(jiān)聽者
            cls_listeners.append(listener)

接下來(lái)注冊(cè)后,這個(gè)listeners就會(huì)一直跟著這個(gè)Signal類。只需要在需要操作關(guān)鍵代碼的地方,進(jìn)行調(diào)用即可。

看看在模型save的時(shí)候,都干了什么?

    async def save(
        self,
        using_db: Optional[BaseDBAsyncClient] = None,
        update_fields: Optional[Iterable[str]] = None,
        force_create: bool = False,
        force_update: bool = False,
    ) -> None:
        ...
        # 執(zhí)行保存前的信號(hào)
        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
        # 執(zhí)行保存后的信號(hào)
        await self._post_save(db, created, update_fields)

拋開其他代碼,可以看到,在模型save的時(shí)候,其實(shí)是先執(zhí)行保存前的信號(hào),然后執(zhí)行保存后的信號(hào)。

自己實(shí)現(xiàn)一個(gè)信號(hào)

有了以上的經(jīng)驗(yàn),可以自己實(shí)現(xiàn)一個(gè)信號(hào),比如我打算做個(gè)數(shù)據(jù)處理器的類,我想在這個(gè)處理器工作中,監(jiān)聽處理前/后的信號(hào)。

# -*- coding: utf-8 -*-
from enum import Enum
from typing import Callable, Dict
# 聲明枚舉信號(hào)量
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):
        """注冊(cè)監(jiān)聽者"""
        # 判斷是否已經(jīng)存在監(jiān)聽者
        cls_listeners = cls._listeners.get(signal).setdefault(cls, [])
        if listener not in cls_listeners:
            # 如果不存在,則添加監(jiān)聽者
            cls_listeners.append(listener)
    def _before_process(self):
        # 取出before_process監(jiān)聽者
        cls_listeners = self._listeners.get(Signals.before_process, {}).get(self.__class__, [])
        for listener in cls_listeners:
            # 調(diào)用監(jiān)聽者
            listener(self.__class__, self)
    def _after_process(self):
        # 取出after_process監(jiān)聽者
        cls_listeners = self._listeners.get(Signals.after_process, {}).get(self.__class__, [])
        for listener in cls_listeners:
            # 調(diào)用監(jiān)聽者
            listener(self.__class__, self)
class SignalModel(Model):
    def process(self):
        """真正的調(diào)用端"""
        self._before_process()
        print("Processing")
        self._after_process()
# 注冊(cè)before_process信號(hào)
@before_process(SignalModel)
def before_process_listener(*args, **kwargs):
    print("before_process_listener1", args, kwargs)
# 注冊(cè)before_process信號(hào)
@before_process(SignalModel)
def before_process_listener(*args, **kwargs):
    print("before_process_listener2", args, kwargs)
# 注冊(cè)after_process信號(hào)
@after_process(SignalModel)
def before_process_listener(*args, **kwargs):
    print("after_process_listener", args, kwargs)
if __name__ == '__main__':
    sm = SignalModel()
    sm.process()

輸出結(jié)果:

before_process_listener1 (<class '__main__.SignalModel'>, <__main__.SignalModel object at 0x7ff700116e50>) {}
before_process_listener2 (<class '__main__.SignalModel'>, <__main__.SignalModel object at 0x7ff700116e50>) {}
Processing
after_process_listener (<class '__main__.SignalModel'>, <__main__.SignalModel object at 0x7ff700116e50>) {}

總結(jié)

筆者通過(guò)對(duì)`tortoise-orm`源碼的學(xué)習(xí),抽絲剝繭,提取了信號(hào)實(shí)現(xiàn)的方式。其核心就是通過(guò)一個(gè)字典存儲(chǔ)調(diào)用方自定義的process方法,然后分別在真正的調(diào)用端的前/后觸發(fā)這些自定義方法即可。

以上就是Tortoise-orm信號(hào)實(shí)現(xiàn)及使用場(chǎng)景源碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Tortoise orm信號(hào)場(chǎng)景的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Python3中的迭代器和生成器及其區(qū)別

    詳解Python3中的迭代器和生成器及其區(qū)別

    本篇將介紹Python3中的迭代器與生成器,描述可迭代與迭代器關(guān)系,并實(shí)現(xiàn)自定義類的迭代器模式。非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-10-10
  • Python實(shí)現(xiàn)的HMacMD5加密算法示例

    Python實(shí)現(xiàn)的HMacMD5加密算法示例

    這篇文章主要介紹了Python實(shí)現(xiàn)的HMacMD5加密算法,簡(jiǎn)單說(shuō)明了HMAC-MD5加密算法的概念、原理并結(jié)合實(shí)例形式分析了Python實(shí)現(xiàn)HMAC-MD5加密算法的相關(guān)操作技巧,,末尾還附帶了Java實(shí)現(xiàn)HMAC-MD5加密算法的示例,需要的朋友可以參考下
    2018-04-04
  • 為Python的Tornado框架配置使用Jinja2模板引擎的方法

    為Python的Tornado框架配置使用Jinja2模板引擎的方法

    Jinja2是人氣Web框架Flask中的內(nèi)置模板引擎,而且與Django的模板引擎比較類似,這里我們就來(lái)看一下為Python的Tornado框架配置使用Jinja2模板引擎的方法
    2016-06-06
  • Python爬蟲實(shí)例之2021貓眼票房字體加密反爬策略(粗略版)

    Python爬蟲實(shí)例之2021貓眼票房字體加密反爬策略(粗略版)

    這篇文章主要介紹了Python爬蟲實(shí)例之2021貓眼票房字體加密反爬策略(粗略版),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • 使用tensorflow進(jìn)行音樂(lè)類型的分類

    使用tensorflow進(jìn)行音樂(lè)類型的分類

    這篇文章主要介紹了使用tensorflow進(jìn)行音樂(lè)類型的分類,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Flask SocketIO實(shí)現(xiàn)動(dòng)態(tài)繪圖的示例詳解

    Flask SocketIO實(shí)現(xiàn)動(dòng)態(tài)繪圖的示例詳解

    Flask-SocketIO 是基于 Flask 的一個(gè)擴(kuò)展,用于簡(jiǎn)化在 Flask 應(yīng)用中集成 WebSocket 功能,本文主要介紹了Flask SocketIO如何實(shí)現(xiàn)動(dòng)態(tài)繪圖,需要的可以參考下
    2023-11-11
  • Python 對(duì)象中的數(shù)據(jù)類型

    Python 對(duì)象中的數(shù)據(jù)類型

    對(duì)于python,一切事物都是對(duì)象,程序中存儲(chǔ)的所有數(shù)據(jù)都是對(duì)象,對(duì)象基于類創(chuàng)建。這篇文章主要介紹了Python 對(duì)象之?dāng)?shù)據(jù)類型 ,需要的朋友可以參考下
    2017-05-05
  • Django中使用ModelForm生成HTML標(biāo)簽的方法步驟

    Django中使用ModelForm生成HTML標(biāo)簽的方法步驟

    在 Django 中,使用 ModelForm 來(lái)生成 HTML 表單標(biāo)簽是一種常見且高效的做法,本文主要介紹了Django中使用ModelForm生成HTML標(biāo)簽的方法步驟,感興趣的可以了解一下
    2024-01-01
  • Qt6中重大改變的QtMultimedia多媒體模塊實(shí)現(xiàn)

    Qt6中重大改變的QtMultimedia多媒體模塊實(shí)現(xiàn)

    本文主要介紹了Qt6中重大改變的QtMultimedia多媒體模塊實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Python?庫(kù)?PySimpleGUI?制作自動(dòng)化辦公小軟件的方法

    Python?庫(kù)?PySimpleGUI?制作自動(dòng)化辦公小軟件的方法

    Python?在運(yùn)維和辦公自動(dòng)化中扮演著重要的角色,PySimpleGUI?是一款很棒的自動(dòng)化輔助模塊,讓你更輕松的實(shí)現(xiàn)日常任務(wù)的自動(dòng)化,下面通過(guò)本文給大家介紹下Python?庫(kù)?PySimpleGUI?制作自動(dòng)化辦公小軟件的過(guò)程,一起看看吧
    2021-12-12

最新評(píng)論

右玉县| 安阳市| 龙里县| 定安县| 仁寿县| 寻乌县| 佛学| 桂平市| 中西区| 色达县| 岑巩县| 阿克陶县| 新巴尔虎左旗| 怀宁县| 佛学| 朝阳县| 龙里县| 盐边县| 泸水县| 巩留县| 临江市| 于都县| 高密市| 景洪市| 通许县| 鹤岗市| 丰县| 日照市| 凤阳县| 林周县| 长乐市| 林甸县| 张家口市| 邯郸县| 海原县| 南郑县| 瑞丽市| 拜泉县| 海丰县| 巴马| 榕江县|