django特定model保存记录时触发动作
当REST接口提交了model记录之后,希望能够触发一个特定的操作。
Django支持定制save()相关的操作哦,例如使用post_save()信号或者 pre_save()。
在models.py中添加
@receiver(pre_save, sender=MainModel)
def save_a_historicmodel(sender, **kwargs):
#do your save historicmodel logic here或者
def save_a_historicmodel(sender, instance, created, **kwargs):
print "Post save was triggered! Instance:", instance
signals.post_save.connect(save_a_historicmodel, sender=MainModel)这样,每次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方法后触发:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
def save_profile(sender, instance, **kwargs):
instance.profile.save()
post_save.connect(save_profile, sender=User)
contrib意思是捐献,通常捐献的开源代码位于这个目录下
以上案例中,save_profile就是接收者功能模块,User是发送者而post_save是信号。可以解读为:每次当一个User实例完成执行它的save方法后,则save_profile功能就被执行。
如果抑制sender参数,类似:post_save.connect(save_profile),则save_profile功能就会在任何Django模型执行save方法的时候执行。
另一种注册信号的方法是使用@receiver装饰器(decorator)
def receiver(signal, **kwargs)这里signal参数可以是一个信号实例(Signal instance)或者是一个信号实例的列表/元组(a list/tuple of Signal instances)。
装饰器(decorator)是指代码运行期间动态添加功能的方式(不修改函数的定义) - 廖雪峰的Python 2.7教程:装饰器
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()如果希望注册 receiver function到不同的信号,可以如下:
@receiver([post_save, post_delete], sender=User)Django Signal代码的位置
根据你注册应用程序信号的不同位置,可能由于导入代码产生不同的边缘影响。所以,建议避免将Signal代码放在models方法或者应用程序的根方法中。
使用@receiver()装饰器
@receiver()装饰器Django文档建议将信号存放在app的配置文件。以下是一个名为profiles的Django app:
profiles/signals.py:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from cmdbox.profiles.models import Profile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()profiles/app.py:
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class ProfilesConfig(AppConfig):
name = 'cmdbox.profiles'
verbose_name = _('profiles')
def ready(self):
import cmdbox.profiles.signals # noqaprofiles/__init__.py:
default_app_config = 'cmdbox.profiles.apps.ProfilesConfig'在以上案例中,只时在ready()方法中导入的signals模块会工作,因为这里使用了@receiver()装饰器。如果使用connect()方式来连接receiver function,则参考以下案例。
使用connect()方法
connect()方法profiles/signals.py:
from cmdbox.profiles.models import Profile
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()profiles/app.py:
from django.apps import AppConfig
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from cmdbox.profiles.signals import create_user_profile, save_user_profile
class ProfilesConfig(AppConfig):
name = 'cmdbox.profiles'
verbose_name = _('profiles')
def ready(self):
post_save.connect(create_user_profile, sender=User)
post_save.connect(save_user_profile, sender=User)profiles/__init__.py:
default_app_config = 'cmdbox.profiles.apps.ProfilesConfig'instance使用(补充)
instance使用(补充)现在有一个问题,就是如果通过Signals触发了某个动作,而这个动作恰好就是要处理刚才Model中post_save保存的那行记录,该如何处理呢?
这里甬道的就是instance,这个instance就是刚才保存的那行数据库记录。举例:
signals.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db.models.signals import post_save
from django.dispatch import receiver
from notify.views import send_msg
from .models import PizzaCooked
@receiver(post_save, sender=PizzaCooked)
def create_pizzacooked_notify(sender, instance, created, **kwargs):
if created:
customer = instance.customer
pizza_name = instance.pizza_name
pizza_size = instance.pizza_size
cook_time = instance.cook_time
msg = "通知:\r亲爱的%s,您订购的 %s %s 披萨已经于 %s 制作完成,请取用" % (customer, pizza_name, pizza_size, cook_time)
send_msg(msg)以上案例,即通过Signal的post_save触发消息发送,其中消息发送内容就是从instance也就是数据库插入数据字段组合得到。
Django内建信号
以下是一些有用的Django信号(常用)
Model Signals
django.db.models.signals.
pre_init:
receiver_function(sender, *args, **kwargs)django.db.models.signals.
post_init:
receiver_function(sender, instance)django.db.models.signals.
pre_save:
receiver_function(sender, instance, raw, using, update_fields)django.db.models.signals.
post_save:
receiver_function(sender, instance, created, raw, using, update_fields)django.db.models.signals.
pre_delete:
receiver_function(sender, instance, using)django.db.models.signals.
post_delete:
receiver_function(sender, instance, using)django.db.models.signals.
m2m_changed:
receiver_function(sender, instance, action, reverse, model, pk_set, using)Request/Response Signals
django.core.signals.
request_started:
receiver_function(sender, environ)django.core.signals.
request_finished:
receiver_function(sender, environ)django.core.signals.
got_request_exception:
receiver_function(sender, request)完整列表参考官方文档
参考
How to Create Django Signals - 非常详尽的参考文档,本文根据这篇博文翻译整理
Last updated
Was this helpful?