django:信号机制

环境:

  • win 10专业版
  • django 4.0
  • pycharm pro 2021.2.3

一,django信号

(一)什么是信号

通俗来说,信号就是通信双方约定的一种信息通知方式,双方通过信号来确定发生了什么事情,然后决定自己应该做什么:

  • 交通管理部门通过红绿灯告知司机与行人当前路口的通行许可。
  • 舰队之间通过旗语传递信息。

看得出,信号最基本的作用就是一方通知另一方发生了什么。

Django有一个信号调度器(signal dispatcher),用来帮助解耦的应用获知框架内任何其他地方发生的操作。简单地说,信号允许某些发送器去通知一组接收器某些操作发生了。当许多代码段都可能对同一事件感兴趣时,信号特别有用。
—— Django Documentation: Signals

(二)信号的使用场景

1,信号的直接使用场景

通信双方必须都在关注同一个主题:

  • 交通管理部门、司机与行人都关注安全。
  • 舰队之间关注船舶航行信息。

而 Django 框架内的组件或应用共同关注的有请求与响应、用户的登录和注销、模型的操作、任务管理、测试、数据库的管理等。为此,Django 提供了 a set of built-in signals

一些使用场景:

  • 用户登陆后,系统向他发送最新动态信息。
  • 数据库数据发生变化后,实现缓存数据同步变化、实现数据审计等
  • 订单中商品数量影响库存数量,即不同模型的联动更新。

2,使用信号的终极目的

这是一个应用程序耦合的 Django 项目。黄色箭头显示哪些应用程序相互导入内容:

在这里插入图片描述这个项目紧密耦合的原因是应用程序之间存在循环依赖关系,导致无法在不破坏其他应用程序的情况下删除某个一个应用程序,因此应该避免这种循环依赖。

这是一个有单一依赖流的项目:

在这里插入图片描述这个项目的结构要好得多,但实现单一流程并不总是那么简单。这就是信号可以提供帮助的地方——使用信号来避免引入循环依赖,这就能够帮助解耦的应用获知框架内任何其他地方发生的操作。

信号的存在,避免让好不容易解耦的应用的耦合度再次变高——简单胜于复杂

3,使用原则

如果一个应用程序想要触发它引用的应用程序中的行为,则不要使用信号,而是直接导入它所需要的行为。

如果一个应用程序想要触发依赖于该应用程序的应用程序中的行为,则可以在第二个应用程序中用接收器接收第一个应用程序发送给它的信号。

如果信号接收器要处理大量I/O操作,也不要使用信号机制,因为它基于同步实现。

When to Use Django Signals

二,如何使用信号

先从源码解读信号,了解各部分关系后再自定义信号,最后讲如何使用django准备好的内置的信号。

(一)自定义与使用信号

所有的信号都是 django.dispatch.Signal 的实例,源码如下:

class Signal:
    """
    所有信号的基类。

    内部属性:
        receivers
            { receiverkey (id) : weakref(receiver) }
    """
    def __init__(self, use_caching=False):
        """
        创建一个新信号。
        """
        self.receivers = []
        if providing_args is not None:
            warnings.warn(
                'The providing_args argument is deprecated. As it is purely '
                'documentational, it has no replacement. If you rely on this '
                'argument as documentation, you can move the text to a code '
                'comment or docstring.',
                RemovedInDjango40Warning, stacklevel=2,
            )
        self.lock = threading.Lock()
        self.use_caching = use_caching
        # 为了方便起见,我们创建空缓存,即使它们没有被使用。
        # 关于缓存的一个注意事项:如果定义了use_caching,那么对于每个不同的发送方,我们缓存发送方在'sender_receivers_cache'中的接收方。当调用.connect()或.disconnect()并在send()中填充时,缓存将被清除。
        self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {
   
   }
        self._dead_receivers = False

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
       """
        将信号的接收器连接到发送器。

        参数:
            receiver
                用于接收信号的函数或实例方法。
                接收器必须是 hashable objects。
                如果weak为True,那么receiver必须是弱引用。
                接收器必须能够接受关键字参数。
                如果一个接收器在连接时使用了一个dispatch_uid参数,那么如果已经有接收器在连接时使用了这个dispatch_uid,那么该参数将不会被添加。

            sender
            	接收器应该响应的发送器。必须是一个Python对象,或None以让接收器接收来自任何接收器的事件。
            weak
                是否对接收器使用弱引用。默认情况下,模块将尝试使用接收器对象的弱引用。如果该参数为false,则将使用强引用。

            dispatch_uid
                一个标识符,用于唯一地标识接收器的特定实例。这通常是一个字符串,尽管它可以是任何 hashable objects。
        """
        from django.conf import settings

        # 如果DEBUG是打开的,检查是否得到一个良好的接收器。
        if settings.configured and settings.DEBUG:
            assert callable(receiver), "Signal receivers must be callable."

            # 检查关键字参数 **kwargs
            if not func_accepts_kwargs(receiver):
                raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")

        if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))

        if weak:
            ref = weakref.ref
            receiver_object = receiver
            # 检查绑定的方法。
            if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
                ref = weakref.WeakMethod
                receiver_object = receiver.__self__
            receiver = ref(receiver)
            weakref.finalize(receiver_object, self._remove_receiver)

        with self.lock:
            self._clear_dead_receivers()
            if not any(r_key == lookup_key for r_key, _ in self.receivers):
                self.receivers.append((lookup_key, receiver))
            self.sender_receivers_cache.clear()

    def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
        """
        断开接收器到发送端的连接。
		
		如果使用弱引用,则不需要调用disconnect。接收器将自动从发送中移除。

        参数:
            receiver
                要断开连接的已注册接收器。如果指定dispatch_uid,则可能为none。

            sender
                要断开连接的已注册发送器。

            dispatch_uid
                要断开连接的接收器的唯一标识符。
        """
        if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))

        disconnected = False
        with self.lock:
            self._clear_dead_receivers()
            for index in range(len(self.receivers)):
                (r_key, _) = self.receivers[index]
                if r_key == lookup_key:
                    disconnected = True
                    del self.receivers[index]
                    break
            self.sender_receivers_cache.clear()
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值