Django信号
在开发过程中,可能有写情况没有考虑进去,突然想新增一些行为,这时不想改变原始接口,怎么对这些接口进行扩展呢
- Django为我们提供了一种信号处理的方式,通过监听某个model的行为,然后触发我们希望预期执行的效果
先看一个简单的例子
这里假设读者已经掌握了如果创建模型,以及对模型的一些基本的操作
我们通过IDE或命令创建好的Django项目结构大概都是这样的
项目名
|__app
|__|__apps.py
|__|__admin.py
|__|__tests.py
|__|__models.py
|__|__views.py
|__|__...
- 假设这是最初设计的model,我们根据这个model写了一些接口
- models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255, verbose_name='类名')
builtin = models.BooleanField(verbose_name='内置属性', blank=True,
default=False)
index = models.IntegerField(verbose_name='索引', blank=True, null=True,
default=1)
class Meta:
verbose_name = '类目'
verbose_name_plural = '类目'
db_table = 'category'
- 现在业务需求变了,我们需要对model新增一个类型字段
- models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=255, verbose_name='类名')
builtin = models.BooleanField(verbose_name='内置属性', blank=True,
default=False)
type = models.CharField(max_length=255, verbose_name='类型', blank=True, null=True)
index = models.IntegerField(verbose_name='索引', blank=True, null=True,
default=1)
class Meta:
verbose_name = '类目'
verbose_name_plural = '类目'
db_table = 'category'
- 假设有两种类型,A和B,我们希望给所有之前的数据都使用默认类型A。新增的数据使用新类型B
这个时候,你可能想说,直接改表结构不就行了,
好吧,我们换个方式,当我们改变类型时,还需要触发一些其它行为,比如通知某些人
我们先看下实现过程
- 首先我们得在settings注册app,
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app', # 新注册的app,通常这样也能用
]
再创建一个监听触发行为的模块signal.py
"""
这里我只举个例子,还有更多类型的信号,都在在signals模块里找到,也可以自定义,参照里面的已定义好的信号方式定义就行
通常自带的信号都是Django自动会帮我们触发的,这个自动不是说任何时候随意的触发,而是只在我们期望的事件发生时才触发该信号
post_init 通常是在model已经执行了__init__方法之后才触发的行为,对应的就有prev_init,初始化之前触发的行为
选这个post_init信号是因为我们希望用户只是在查看才去检查这个model类型,因为如果用户是去修改的话,我们其实已经在修改过程处理好了这个类型
"""
from app.models import Category
from django.db.models.signals import post_init
def snapshot_init(sender, **kwargs):
instance = kwargs.get('instance', None)
# 由于这个信号在实例化后触发,就不分是否之前已经创建入库,还是新创建的对象,所以我们需要进行控制,只针对已经创建的对象
# 区分他们只有pk了,没有创建的对象pk为None的。
# 新创建的对象为什么不需要处理,既然需求提了,我们肯定已经对新创建这部分加上这个逻辑处理了,所以只对需求变更之前的数据进行处理
if not instance.pk:
return
# 这里处理进行一些逻辑处理
# instance就是实例化后的model对象,
post_init.connect(snapshot_init, sender=Category)
-
定义好信号,怎么触发呢,首先可以肯定的是不能任意位置导入这个信号执行,得在Django初始化所有app后才能开始监听我们的信号
-
apps.py这个文件就是为这个而设计的
# apps.py
from django.apps import AppConfig
class AppConfig(AppConfig):
name = 'app'
def ready(self):
# 从这里导入之前定义的信号模块
from app import signal
- 然后我们启动Django,等着信号触发吧
。。。
-
等了半天也没见信号触发,问题出在哪,Django从1.8之后注册app形式已经改为下面这种方式了
-
当然我们这里只是示例,实际项目中,大家就不要取app这个名称了,否则上面那种AppConfig继承形式容易使人困惑
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app.apps.AppConfig', # 新注册的app,通常这样也能用
]
- 到这里就完了