1 Introduction

signal

1.1 Intro

“信号分发器”, 允许”解耦”应用在其他地方发生操作时触发信号, 大大降低代码耦合度. 主要通过sender/receiver来完成.

Django的该信号机制类似”观察者模式”, 称为publish-subscribe, 绑定信号, 发送信息, 监听函数收到信号后执行.

1.2 Built-in

  • 在保存model之前/之后触发: pre_save, post_save
  • 在删除model之前/之后触发: pre_delete, post_delete
  • 在http建立/关闭时触发: request_started, request_finished

2 Built-in Signal Step

2.1 Introduction

  • Listen: 函数connect()完成注册工作, 类似TCP-listen.
  • Receiver: callback函数, 对信号的处理, 类似TCP-accept函数
  • Sender: 调用者, 根据注册的信号, 触发某一个信号, 类似TCP-write()

2.2 Register

进行信号的注册:

1
2
3
4
5
6
7
8
9
10
# 1 connect函数: Signal.connect(receiver, sender, weak, dispatch_uid
# receiver: 信号连接的回调函数
# sender: 从哪里发出信号
# dispatch_uid: 某一个信号的唯一标识码, 用于放置重复发送信号, 默认为 None

# 2 receiver装饰器
from django.dispatch import receiver
@receiver(post_save)
def receiver_func(sender, **kwargs):
pass

2.3 Example

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1 定义信号,例如自定义信号post_save
from django.db.models.signals import post_save

# 2 注册信号, 在apps.user.appconfig中完成
post_save.connect(receiver_func, sender=settings.AUTH_USER_MODEL)

# 3 处理信号, 避免在模块中导入model, sender为发送者
def receiver_func(sender, **kwargs):
from apps.user.models import AuthToken
# handle some thing

# 4 发送built-in signal, 会在 AUTH_USER_MODEL 保存时自动触发
user.save()

3 Defined Signal

3.1 Defined

定义非build-in信号, 设置参数, 注意, 尽量确保信号传递的参数大小, 毕竟信号一般
都是与队列等同步内存挂钩, 尽可能极少此共享内存的使用:

1
2
3
import django.dispatch
# 信号: my_signal
my_signal_print = django.dispatch.Signal(providing_args=["uid", "name"])

3.2 Connect

绑定un-built-in信号的处理函数:

1
2
from my_signal import my_signal_print
my_signal_print.connect(print_my_username)

3.3 Sender

发送信号:

1
2
3
4
5
from my_signal import my_signal_print
class SenderClass(object):
...
def send_user(self):
my_signal_print.send(self.__class__, uid=self.uid, name=self.name)

3.4 Disconnect

断开信号的接收器:

1
2
from my_signal import my_signal_print
my_signal_print.disconnect()

4 Django-rq

django-rq

4.1 Intro

使用django-rq封装的信号处理机制, 方便快捷的创建信号, 其中django-rq基于redis来进行
信号的传递, 具体配置见官网.

4.2 Create and Listen

创建信号, 绑定信号接收函数:

1
2
3
4
5
from django-rq import job

@job('rq_value1')
def print_my_username(uid, name, **kwargs):
pass

4.3 Sender

发送信号:

1
2
from my_signal import print_my_username
print_my_username.delay(uid, name)

5 Applications and Signal

5.1 Introduction

app

开发者主要使用该功能进行app的自定义配置工作, 默认情况下无需进行此类操作.

这里主要结合signals完成自定义信号的处理, 利用Appconfig的自定义配置和自省功能.

AppConfig信息见源码django/apps/config.py, 非常简单.

5.2 Configure

对于任何一个app, django会检查INSTALL_APPS中的每一个模块是否存在default_app_config
变量:

  • 存在该变量, 则其值为: 该模块 Appconfig 子类的路径
  • 不存在, 默认为Appconfig

所以, 一般在每一个模块的init.py中定义该变量值.

5.3 应用开发

在某一个应用下设置default_app_config, 进行自定义配置, 可以在指定的路径中的子类中
注册信号.

my_app/init.py:

1
default_app_config = 'my_app.appconfig.MyConfig'

my_app/appconfig.py:

1
2
3
4
5
6
7
8
9
from django.apps import AppConfig

class MyConfig(AppConfig):
name = 'my_app'
verbose_name = 'My App Name'

def ready(self):
from my_signal import my_signal_print
my_signal_print.connect(print_my_username)