Django signal

这不是大神的随笔,只是记忆力不好的码农笔记

参考:https://segmentfault.com/a/1190000008455657
参考:https://blog.csdn.net/laughing2333/article/details/53159109

什么是django的signal

官方文档描述如下:
Django includes a “signal dispatcher” which helps allow decoupled applications get notified when actions occur elsewhere in the framework.In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place. They’re especially useful when many pieces of code may be interested in the same events.
Django内部包含了一位“信号调度员”:当某事件在框架内发生时,它可以通知到我们的应用程序。 简而言之,当event(事件)发生时,signals(信号)允许若干 senders(寄件人)通知一组 receivers(接收者)。这在我们多个独立的应用代码对同一事件的发生都感兴趣时,特别有用。

最佳使用场景

通知类
通知是signal最常用的场景之一。例如,在论坛中,在帖子得到回复时,通知楼主。从技术上来讲,我们可以将通知逻辑放在回复保存时,但是这并不是一个好的处理方式,这样会时程序耦合度增大,不利于系统的后期扩展维护。如果我们在回复保存时,只发一个简单的信号,外部的通知逻辑拿到信号后,再发送通知,这样回复的逻辑和通知的逻辑做到了分开,后期维护扩展都比较容易。

初始化类
信号的另一个例子便是事件完成后,做一系列的初始化工作

其他一些使用场景总结

以下情况不要使用signal:

  • signal与一个model紧密相关,并能移到该model的save()时
  • signal能使用model manager代替时
  • signal与一个view紧密相关,并能移到该view中时

以下情况可以使用signal

  • signal的receiver需要同时修改对多个model时
  • 将多个app的相同signal引到同一receiver中处理时
  • 在某一model保存之后将cache清除时
  • 无法使用其他方法,但需要一个被调函数来处理某些问题时

如何使用

django的signal使用可分为2个模块

  • signal:signal定义及触发事件
  • receiver:signal接受函数

内建signal的使用

django内部有些定义好的signal供我们使用
模型相关:

  • prec_save 对象save前触发
  • post_save 对象save后触发
  • pre_delete 对象delete前触发
  • post_delete 对象delete后触发
  • m2m_changed ManyToManyField 字段更新后触发

请求相关:

  • request_started 一个request请求前触发
  • request_finished request 请求后触发

针对django自带的signal,我们只需要编写receiver即可,使用如下。
如果想要一个函数或者一个实例方法订阅一个信号,Django Signals提供了两种方法

  • 使用receiver装饰器
  • 使用signal实例的connect方法
  1. 编写receiver并绑定到signal
    myapp/signals/handlers.py

    from django.dispatch import receiver
    from django.core.signals import request_finished
     
    ## 方式一绑定方式
    @receiver(request_finished, dispatch_uid="request_finished")
    def my_signal_handler(sender, **kwargs):
        print("Request finished!================================")sender
    
    # 方式二绑定方式
    def my_signal_handler(sender, **kwargs):
        print("Request finished!================================")
    
    request_finished.connect(my_signal_handler)
    
    #####################################################
    # 针对model 的signal 
    from django.dispatch import receiver
    from django.db.models.signals import post_save
     
    from polls.models import MyModel
     
     
    @receiver(post_save, sender=MyModel, dispatch_uid="mymodel_post_save")
    def my_model_handler(sender, **kwargs):
     print('Saved: {}'.format(kwargs['instance'].__dict__))
    
    

    为了防止一个信号被同样的函数或者实例方法多次订阅,可以使用一个dispatch_uid参数来标记一个函数或者实例方法,dispatch_uid确保此receiver只调用一次

  2. 第二步,加载signal
    myapp/__init__py

    default_app_config = 'myapp.apps.MySendingAppConfig'
    

    myapp/apps.py

    from django.apps import AppConfig
    
    class MyAppConfig(AppConfig):
        name = 'myapp'
        def ready(self):
            # signals are imported, so that they are defined and can be used
            import myapp.signals.handlers
    

到此,当系统受到request请求完成后,便会执行receiver
其他内建的signal,参考官方文档
https://docs.djangoproject.com/en/1.9/topics/signals/

自定义signal的使用

自定义signal,需要我们编写signal和receiver
一、使用receiver装饰器

  1. 编写signal
    myapp.signals.signals.py
    import django.dispatch
     
    my_signal = django.dispatch.Signal(providing_args=["my_signal_arg1", "my_signal_arg2"])
    
    在Signal实例化中的providing_args声明了订阅这个signal的recevier会接收到哪些关键字参数,但是Django Signals并不会对这个参数是否准确进行检查,也就是说即使在调用send方法的时候如果传入了一个没有在providing_args中定义的关键字,Django也不会报错
  2. 加载signal
    settings中注册app
    INSTALLED_APPS = [
    'myapp.apps.MyappConfig'
    ]
    
    由于在django.setup()的过程中,它会遍历settings.INSTALLED_APPS列表中的每一项,并调用该AppConfig的ready方法,因此,将recevier订阅signal的过程放置于ready方法中就能保证该代码的执行
    myapp/apps.py
    from django.apps import AppConfig
    class MyappConfig(AppConfig):
        name = 'myapp'
     
        def ready(self):
            # signals are imported, so that they are defined and can be used
            import myapp.signals.handlers
    
  3. 事件触发时,发送signal
    myapp/views.py
    from .signals.signals import my_signal
    def send_sign(request):
        my_signal.send(sender="some function or class",
            my_signal_arg3="something", my_signal_arg4="something else")
    
    sender必须有,默认为None,后面的参数随便传,signal不会检验
  4. 收到signal,执行receiver
    myapp/signals/handlers.py
    from django.dispatch import receiver
    from myapp.signals.signals import my_signal
     
    @receiver(my_signal, dispatch_uid="my_signal_receiver")
    def my_signal_handler(sender, **kwargs):
        print('my_signal received')
    

二、使用connect来订阅信号

  1. 编写signal
    myapp.signals.signals.py
    from django.dispatch import Signal
    my_signal = Signal(providing_args=[])
    
    在Signal实例化中的providing_args声明了订阅这个signal的recevier会接收到哪些关键字参数,但是Django Signals并不会对这个参数是否准确进行检查,也就是说即使在调用send方法的时候如果传入了一个没有在providing_args中定义的关键字,Django也不会报错
  2. 加载signal
    settings中注册app
    INSTALLED_APPS = [
    'myapp.apps.MyappConfig'
    ]
    
    由于在django.setup()的过程中,它会遍历settings.INSTALLED_APPS列表中的每一项,并调用该AppConfig的ready方法,因此,将recevier订阅signal的过程放置于ready方法中就能保证该代码的执行
    myapp/apps.py
    from django.apps import AppConfig
    from myapp.signals.signals import my_signal
    from myapp.signals.handlers import my_signal_handler
    
    class MyappConfig(AppConfig):
        name = 'myapp'
        def ready(self):
            my_signal.connect(my_signal_handler, dispatch_uid='my_sign_receiver')
    
  3. 事件触发时,发送signal
    myapp/views.py
    from django.http import HttpResponse
    from myapp.signals.signals import my_signal
    
    
    def send_sign(request):
        my_signal.send(__name__)
        return HttpResponse("ok")
    
  4. 收到signal,执行receiver
    myapp/signals/handlers.py
    def my_signal_handler(sender, **kwargs):
    	print("hello world")
    

总结

django signal的处理是同步的,勿用于处理大批量任务。
django signal对程序的解耦、代码的复用及维护性有很大的帮助

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值