Django signal 信号机制的使用

本文介绍了如何利用Django的信号机制解决Celery任务执行完毕后重复发送邮件的问题。通过监测`PdfTask`模型中`is_create`字段变化,当任务状态变为完成时,触发邮件发送。在邮件发送过程中,先检查邮件是否已发送,避免重复发送。同时,文章列举了Django中不同类型的信号,如`post_save`,并展示了如何在模型配置中使用这些信号。
摘要由CSDN通过智能技术生成

背景:如果这个任务(定时或者立即)执行完毕,然后把一些执行情况生成pdf,通过邮件发送给客户。
现象:我通过celery周期查看任务的执行状态,如果任务执行完毕,异步更改任务状态,然后执行发送邮件。但是时不时会出现重复发送邮件,原因异步任务重复进入celery任务队列,导致有的任务会重复发送邮件。
解决方案:通过django的signal信号机制来监测任务状态表数据的变更,一旦任务状态变成完成,触发signal信号,然后执行发送邮件逻辑代码。(但是需要注意不要在周期之中反复更改这个状态)

要监控的表结构:

class PdfTask(models.Model):
    """pdf任务表"""
    IS_CREATE_CHOICES = (
        (0, u'未生成'),
        (1, u'生成中'),
        (2, u'已生成'),
        (3, u'异常')
    )
    task_url = models.TextField(verbose_name='任务详情链接')
    pdf_path = models.TextField(verbose_name='pdf的保存的目录')
    pdf_name = models.TextField(verbose_name='pdf的名称')
    foreign_key_id = models.IntegerField(verbose_name='外键的id', null=True)

    is_create = models.IntegerField(verbose_name=u'是否生成', choices=IS_CREATE_CHOICES, default=0)
    result = models.BooleanField(verbose_name='生成的结果', default=False)
    run_num = models.IntegerField(verbose_name='执行生成的次数', default=0)
    is_delete = models.IntegerField(choices=[(0, u'未删除'), (1, u'已删除')], default=0)

    class Meta:
        verbose_name = 'pdf任务表'
        db_table = 't_pdf_task'

在这里主要监测is_create字段的变化,当该字段变成生成状态时

监测加发送邮件代码:

# 采用信号监听 能够确保邮件发送只被触发一次 PDF邮件无论是否生成正确还是不正确的
@receiver(signals.post_save, sender=PdfTask)
def inspect_instance(instance, created, **kwargs):
    if not created and instance.is_create in [2, 3]:
        logger.warning('第一步:触发邮件发送')
        from apps.inspection_tasks.models import InspectionReport
        inspection_report_objs = InspectionReport.objects.filter(id=instance.foreign_key_id)
        if inspection_report_objs:
            inspection_report = inspection_report_objs[0]
            if inspection_report.is_send_email is False:
                logger.warning('第二步:触发邮件状态更改')
                inspection_report.is_send_email = True
                inspection_report.save()
                title = "{}巡检报告-{}".format(
                    inspection_report.data_center.data_center_name,
                    datetime.datetime.strftime(inspection_report.begin_time, '%Y%m%d')
                )
                content = """
                                           亲爱的业务系统管理员:蓝鲸平台于{0}生成{2}系统巡检报告,巡检过程耗时{1}秒。请登录蓝鲸平台,在"系统巡检->巡检报告->巡检历史"中找到当日巡检任务,查看巡检报告内容详情!
                                           """.format(
                    datetime.datetime.strftime(inspection_report.begin_time, '%Y%m%d %H:%M'),
                    inspection_report.use_time,
                    inspection_report.data_center.data_center_name
                )
                receiver = inspection_report.temp_id.receiver
                logger.warning('第三步:触发邮件信息组装')
                pdf_name = instance.pdf_name
                pdf_path = instance.pdf_path
                pwd_file = pdf_path + pdf_name
                with open(pwd_file, 'rb') as f:
                    base64_data = base64.b64encode(f.read())
                    file_data = base64_data.decode()
                attachments = [{
                    "filename": pdf_name,
                    "content": file_data
                }]
                from apps.commons.cmsi_handle import send_email
                logger.warning('第四步:执行邮件信息发送')
                send_email(title, content, receiver, attachments)
                logger.warning('第五步:执行邮件信息发送完毕')

在这里使用的是Django内置signal中的model signal
比较常用的signal 类型
1.model signal类型

pre_init                        # Django中的model对象执行其构造方法前,自动触发
post_init                       # Django中的model对象执行其构造方法后,自动触发
pre_save                        # Django中的model对象保存前,自动触发
post_save                       # Django中的model对象保存后,自动触发
pre_delete                      # Django中的model对象删除前,自动触发
post_delete                     # Django中的model对象删除后,自动触发

2.初始化时使用的signal类型

pre_migrate                     # 执行migrate命令前,自动触发
post_migrate                    # 执行migrate命令后,自动触发 

例如:

# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

from django.apps import AppConfig
from django.db.models.signals import post_migrate


def app_ready_handler(sender, **kwargs):
    from apps.zabbix.models import ModulesConfig

    print("Modules数据初始化")
    ModulesConfig.init_data()

class ModulesConfigConfig(AppConfig):
    name = 'apps.zabbix'

    def ready(self):
        post_migrate.connect(app_ready_handler, sender=self)

3.请求时使用的signal类型

request_started                 # 请求到来前,自动触发
request_finished                # 请求结束后,自动触发
got_request_exception           # 请求异常时,自动触发

例如:

from django.core.signals import request_finished
from django.dispatch import receive

@receiver(request_finished)
def test(sender, **kwargs):
    print("request finished!!!")

4.创建数据库连接时,自动触发

connection_created              # 创建数据库连接时,自动触发
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值