django特定model保存记录时触发动作
当REST接口提交了model记录之后,希望能够触发一个特定的操作。
Django支持定制save()
相关的操作哦,例如使用post_save()
信号或者 pre_save()
。
在models.py
中添加
或者
这样,每次MainModel
保存时候信号都会触发。
详细文档见 post_save
Django Signals
Django Signals是一个当某些事件发生时,允许解耦的应用程序(decoupled applications
)获得通知的策略。
decoupled applications
- 解耦应用程序低耦合,高内聚---模块之间低耦合,模块内部高内聚。一个系统有多个模块组成,在划分模块时,要把功能关系紧密的放到一个模块中(高内聚),功能关系远的放到其它模块中。模块之间的联系越少越好,接口越简单越好(低耦合,细线通信)。如果划分的模块之间接口很复杂,说明功能划分得不太合理,模块之间的耦合太高了,同时也说明某模块的内聚也不高。 - 程序设计经常提到的解耦,到底是要解除什么之间的耦合?
例如,每次一个给定的模型(Model)实例被更新需要废弃掉一个缓存页面,但是,基于这个模型的一系列位置需要更新。此时可以使用singals
,hook一些代码片段在这个指定model保存动作被触发时执行。
另一种常用的案例是使用Profile策略通过一对一(one-to-one)关系来扩展定制Django User。通常会使用一个信号调度器(signal dispatcher
)来监听用户的post_save
事件以便能够更新Profile实例。 -- 可以参考How to Extend Django User Model
Django Signals工作原理
Observer Design Pattern
(观察者设计模式)
在信号机制中有两个关键因素:发送者和接收者。正如名字所示,发送者是负责调度一个信号,接收者则是负责接受信号并作一些事情。
receiver
接收者必须是一个功能模块(function
)或者是一个实例方法(instance method
)才能接收信号。
sender
发送者必须是一个Python对象,或者是None才能从任何发送者这里接收事件。
连接发送者和接收者之间的是"信号调度器"(signal dispatchers
),是通过connect
方法的Signal
实例。
Django核心已经定义了ModelSignal
,也就是一个Signal
子类,允许发送者以app_label.ModelName
形式快速指定为一个字符串。但是,你需要使用Signal
类来创建定制的信号。
要接收一个信号,需要注册一个receiver
功能,这样当信号被通过Signal.connect()
方法发送的时候来获取调用,
Django Signal使用
这里举例内建信号post_save
。这段代码位于django.db.models.signals
模块。这个信号在模型完成save
方法后触发:
contrib
意思是捐献,通常捐献的开源代码位于这个目录下
以上案例中,save_profile
就是接收者功能模块,User
是发送者而post_save
是信号。可以解读为:每次当一个User
实例完成执行它的save
方法后,则save_profile
功能就被执行。
如果抑制sender
参数,类似:post_save.connect(save_profile)
,则save_profile
功能就会在任何Django模型执行save
方法的时候执行。
另一种注册信号的方法是使用@receiver
装饰器(decorator)
这里signal
参数可以是一个信号实例(Signal
instance)或者是一个信号实例的列表/元组(a list/tuple of Signal
instances)。
装饰器
(decorator)是指代码运行期间动态添加功能的方式(不修改函数的定义) - 廖雪峰的Python 2.7教程:装饰器
如果希望注册 receiver function到不同的信号,可以如下:
Django Signal代码的位置
根据你注册应用程序信号的不同位置,可能由于导入代码产生不同的边缘影响。所以,建议避免将Signal
代码放在models
方法或者应用程序的根方法中。
使用@receiver()
装饰器
@receiver()
装饰器Django文档建议将信号存放在app的配置文件。以下是一个名为profiles
的Django app:
profiles/signals.py
:
profiles/app.py
:
profiles/__init__.py
:
在以上案例中,只时在ready()
方法中导入的signals
模块会工作,因为这里使用了@receiver()
装饰器。如果使用connect()
方式来连接receiver function
,则参考以下案例。
使用connect()
方法
connect()
方法profiles/signals.py
:
profiles/app.py
:
profiles/__init__.py
:
instance
使用(补充)
instance
使用(补充)现在有一个问题,就是如果通过Signals
触发了某个动作,而这个动作恰好就是要处理刚才Model
中post_save
保存的那行记录,该如何处理呢?
这里甬道的就是instance
,这个instance
就是刚才保存的那行数据库记录。举例:
signals.py
以上案例,即通过Signal的post_save
触发消息发送,其中消息发送内容就是从instance
也就是数据库插入数据字段组合得到。
Django内建信号
以下是一些有用的Django信号(常用)
Model Signals
django.db.models.signals.
pre_init
:
django.db.models.signals.
post_init
:
django.db.models.signals.
pre_save
:
django.db.models.signals.
post_save
:
django.db.models.signals.
pre_delete
:
django.db.models.signals.
post_delete
:
django.db.models.signals.
m2m_changed
:
Request/Response Signals
django.core.signals.
request_started
:
django.core.signals.
request_finished
:
django.core.signals.
got_request_exception
:
完整列表参考官方文档
参考
How to Create Django Signals - 非常详尽的参考文档,本文根据这篇博文翻译整理
Last updated