程序解耦的优秀作品,django信号量

内置信号,使用更轻松

1. 从一个http请求开始

首先我们需要在app下编写一个用于接收信号的py文件,不妨文件名为:signal_test.py。

使用下面其中一种方式来接收信号

  •  方式一
from django.core.signals import request_finished
def my_callback(sender, **kwargs):
    print("Request finished!")
request_finished.connect(my_callback)
  •  方式二
from django.core.signals import request_finished
@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

 在当前app下的apps.py文件中,通过AppConfig子类,重写ready函数,在ready函数中导入之前的信号接收文件signal_test.py,比如:现在我的app名称就叫app,则AppConfig子类如下:

from django.apps import AppConfig
class AppConfig(AppConfig):
    name = 'app'
    def ready(self):
        from . import signal_test

然后确保在settings安装的app使用的是如下格式:

INSTALLED_APPS = [
    '……',
    'app.apps.AppConfig'
]

在INSTALLED_APPS如果直接写“app”的话就会导致信号无法正常接收,请注意。

编写一个简单的视图函数,如:

def signal_control(request):
    return HttpResponse("hello world")

访问该视图接口,在请求完成时就可以看到在信号接收器打印的信息,这样就完成了django的内置信号的简单使用

2. 内置信号的种类

本文使用的django版本是2.1.4,在早期的版本中,信号所在的模块位置可能有些不太一样。

  • django.db.models.signals
pre_init       # django的modal执行其构造方法前,自动触发
post_init      # django的modal执行其构造方法后,自动触发
pre_save       # django的modal对象保存前,自动触发
post_save      # django的modal对象保存后,自动触发
pre_delete     # django的modal对象删除前,自动触发
post_delete    # django的modal对象删除后,自动触发
m2m_changed    # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
pre_migrate    # 执行migrate命令前,自动触发
post_migrate   # 执行migrate命令后,自动触发
  • django.core.signals
request_started         # 请求到来前,自动触发
request_finished        # 请求结束后,自动触发
got_request_exception   # 请求异常后,自动触发
setting_changed         # 使修改配置文件时,自动触发
  • django.test.signals
template_rendered   # 使用test测试渲染模板时,自动触发
  • django.db.backends.signals
connection_created   # 创建数据库连接时,自动触发

3. 内置信号详解

从上一个dome中,我们发现了request_finished信号是不发送任何的参数的,尽管这样,信号接收器依旧还是需要参数**kwargs,否则django会报错,请注意。

对于pre_save和post_save信号,经常性的需求的在处理业务之前,需要检查是“新增”还是“更新”,我们对两者做了比对,**kwargs参数如下:

# 新增
{
    'signal': <django.db.models.signals.ModelSignal object at 0x000002A1A16ADFD0>,
    'instance': <User: User object (None)>,
    'raw': False,
    'using': 'default',
    'update_fields': None
 }
# 修改
{
    'signal': <django.db.models.signals.ModelSignal object at 0x000002399970DFD0>, 
    'instance': <User: User object (1)>, 
    'raw': False, 
    'using': 'default', 
    'update_fields': None
}

这里有一个很奇怪的原因,不管是pre_save还是post_save,即使是修改model,打印出来的update_fields依旧是None,对于pre_save,可通过instance来区分,而对于post_save,**kwargs会新增一个新的参数created(如果是新增,则值为False;如果是修改,则值为True)

进一步了解信号

1. 信号监听

要接收信号就需要注册一个用于接受信号的监听器,通过使用Signal.connect()来监听,然后编写一个用于处理信号的函数,通过connect的receiver参数引用来指向该函数。下面对Signal.connect的各个参数做分析:

Signal.connect(receiver[, sender=None, weak=True, dispatch_uid=None])
    receiver:     处理的信号的回调函数
    sender:       指定一个特定的sender,接收来自这个特定的sender的信号
    weak:         
           Django通常以弱引用储存信号处理器。这就是说,如果你的receiver是个局
           部变量,可能会被垃圾回收。当你调用信号的connect()方法时,传递 weak
=False来防止这样做。 dispatch_uid: receiver的唯一标识符,以防信号多次发送,参考防止信号重复。

2. 防止信号重复

在一些情况下,连接receiver 到信号的代码可能会执行多次。这会使你的receiver 函数被注册多次,并且导致它对于同一信号事件被调用多次。首先我们需要确认,这种情况是否是不必要的,如果不必要,则应该在接收信号时即使用Signal.connect给出参数dispatch_uid。dispatch_uid可以是可哈希对象,但通常是一个唯一的字符串。

3. 断开信号

这个目前并不是很常用,如需要则可以调用Signal.disconnect()来断开信号的接收器。Signal.connect()中描述了所有参数。如果接收器成功断开,返回 True,否则返回False。receiver 参数表示要断开的已注册receiver。如果使用dispatch_uid 标识receiver,它可以为None。

4. 自定义信号

Django提供的自定义信号的方式也很简单。主要分为以下步骤

(1)定义一个信号

Django提供了所有信号的实例(即django.dispatch.Signal),用户需要向django.dispatch.Signal提供一个providing_args参数,该参数的类型为list,providing_args中的每个单元就是提供给信号接受者的参数。例如:pizza_done信号向接受者提供toppings和size参数

(2)发送信号

发送信号有两种方式,分别是:
  Signal.send(sender, **kwargs)
  Signal.send_robust(sender, **kwargs)
  发送信号时,我们必须提供的参数是sender,如果之前的定义的信号中有参数toppings和size,

  则也需要提供,sender参数一般来说是一个类(这样方便信号接收)

 send() 和send_robust()都会返回一个含有二元组的列表 [(receiver, response), ... ],它代表了被调用的接收器函数和他们的响应值。send() 与 send_robust()在处理receiver 函数产生的异常时有所不同。send()  不会 捕获任何由receiver 产生的异常。它会简单地让错误往上传递。所以在错误产生的情况,不是所有receiver 都会获得通知。send_robust()捕获所有继承自Python Exception类的异常,并且确保所有receiver 都能得到信号的通知。如果发生错误,错误实例会在产生错误的receiver 的二元组中返回。

(3)接收信号

之前的例子中大多是使用的接受信号的方式的使用Signal.connect,但是我们一般会使用@receiver多一点。

@receiver(pizza_done, sender=PizzaStore)
def my_callback(sender, **kwargs):
    print("接收到来自PizzaStore的信号")

猜你喜欢

转载自www.cnblogs.com/lynn578/p/11870205.html