依旧是做django1.6 python2.7 升级到 django3.2 python3.8的任务。刚好本次任务涉及几个骚情的东西,这里总结一下,一个是 信号,一个是 django自带的api(本篇不介绍,会在下一篇介绍)
首先,先讲讲对信号这个东西的整体理解:
这东西给我的感觉就像是这样的:
本来代码应该是按照顺序执行,从 节点1 直接运行至 节点4 ,但是使用信号这个东西以后,就成了上面的样子,在节点1处,发信号,然后立刻就会被节点2处监测并收到信号,于是执行收到信号后应该干的事,完事后就进入到了节点3处,也是就是说,节点2到节点3就是收到信号后,做事情的过程,这个过程是独立的,是一个新的线程去做的,完全不影响当下代码的执行。
这就是一个信号整体执行的过程。
接下来看看信号这个东西怎么用。
这东西从执行的过程中,不难发现,首先,得有个信号,然后这个信号得绑定一个要做的事情(python中,不就是函数么),最后,就是得有个地方去发信号。于是过程就成了这个样子,在节点1处,发送信号,节点2,3,做与信号绑定的事情。
同样,具体的实现,也差不多是这个步骤:(django本身有自带的信号,这里先讲自定义信号)
# 先自定义一个信号,这里注意,所有的自定义信号都必须是来自于django的Signal模块,就是说,我们所有的自定义信号都是基于django自带的Signal模块来搞的。然后就是给我们的信号起名字,下面示例中起名位
pizza_done
,这里其实说到底,Signal是一个类对象,我们给自定义信号起名字的过程就是创建实例对象的过程
import
django.dispatch
pizza_done
=
django.dispatch.Signal(providing_args
=
[
"toppings"
,
"size"
])
顺便看下Signal的参数:
一般情况,我们都是只用第一个参数,第二个参数都是走默认。
这里注意:第一个参数是指我们给这个信号绑定具体事件(函数)时,这个函数所需要的参数。
# 接下来就是制定事件,并且将事件与信号绑定
def
do_somethings(sender,
*
*
kwargs):
("
do_somethings")
(sender,kwargs)
pizza_done.connect(
do_somethings)
# 最后,就是触发信号
from
xxx.xxx.xxx
import
pizza_done
pizza_done.send(sender
=
'seven'
,toppings
=
123
, size
=
456
)
这里注意,一般情况,都会带,sender 这个参数,因为我们需要知道是谁触发的信号,而且实际应用中,这个sender往往会使个类对象的实例对象。
看一个实际的应用:
这样以来,当服务启动时,在文件加载的时候,就一次性完成了,三个信号对象与相应事件的绑定动作。
这里就是触发发送的动作的地方,这里是django modle模型,重写了save方法,也就是说,在保存一条模型数据的时候,就会触发发送信号的动作,然后,相应的信号就会去做约定好的事情。
这里注意,发送时,使用了 send_robust 方法,这个方法和send大致一样,但是会比send方法更加友好,因为使用send方法,一旦出错,完全是没有相关提示的,而 send_robust 方法,会将相关错误,比较清晰明确的打印到控制台。
上述为自定义信号创建的一种方法,还有一种方法,是采用装饰器直接完成事件的创建和与信号对象的绑定动作。
这种类型,依旧属于自定义信号,使用装饰器 @receiver 直接可以将信号对象,和要做的事件绑定。一旦有地方发送信号,这里就会立刻执行下面的函数。
ok,这就是自定义信号的创建过程。
然后,django本身也有许多内建的信号,比如:
Model signals
pre_init # model初始化前触发,自动触发
post_init # model初始化后触发,自动触发
pre_save # save()方法前触发,自动触发
post_save # save()方法后触发,自动触发
pre_delete # delete()方法前触发,自动触发
post_delete # delete()方法后触发,自动触发
m2m_changed # ManyToManyField字段改变时触发,django的model中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中model类,对于每一个类,自动触发
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
Request/response signals
request_started # 请求开始时触发,自动触发
request_finished # 请求完成后触发,自动触发
got_request_exception # 请求异常时,自动触发
Test signals
setting_changed # 使用test测试修改配置文件时,自动触发
template_rendered # 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created # 创建数据库连接时,自动触发
Django 提供了一系列的内建信号,允许用户的代码获得DJango的特定操作的通知。这包含一些有用的通知:
django.db.models.signals.pre_save & django.db.models.signals.post_save
在模型 save()方法调用之前或之后发送。
django.db.models.signals.pre_delete & django.db.models.signals.post_delete
在模型delete()方法或查询集的delete() 方法调用之前或之后发送。
django.db.models.signals.m2m_changed
模型上的 ManyToManyField 修改时发送。
django.core.signals.request_started & django.core.signals.request_finished
Django建立或关闭HTTP 请求时发送。
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
def callback(sender, **kwargs):
print("pre_save_callback")
print(sender,kwargs)
pre_save.connect(callback) # 该脚本代码需要写到app或者项目的初始化文件中,当项目启动时执行注册代码
# 或者这样
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
接下来看个实例:
我重点解释下,这段代码。这里首先用的是django的内建信号,那么问题来了,内建信号是固定的,唯一的,没法搞什么实例对象,那么如果有多个对象,都触发了内建信号,这咋整,ok,如果这里跟信号绑定的是一个公共操作,大家伙都要执行,那么没问题,都触发信号了,那就都执行相关的具体事件就可以了,但是如果是a对象触发了信号,但是想干这件事,b对象也触发了信号,但是想干另一件事,咋弄,所以新的使用方式就出现了,就是上面的方式。@receiver 里面,第一个参数,就是信号对象本身,第二个参数,sender 就是用来绑定触发事件的对象,比如是a触发的,还是b触发的,那么上述代码的整体意思就变成了,当sender为 User对象的时候,当User对象触发信号对象 builtin_signals.post_save 的时候,执行函数 create_settings。如果是 aaa 也触发了builtin_signals.post_save信号的时候,是不会执行函数 create_settings的。这就实现了,具体事件,具体对待的处理问题的方式。