Django信号

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,通常这样也能用
]
  • 到这里就完了
posted @ 2018-07-13 10:29  曾春云  阅读(144)  评论(0编辑  收藏  举报