经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Django » 查看文章
Django使用Signals监测model字段变化发送通知的一些技巧
来源:jb51  时间:2018/11/20 10:15:31  对本文有异议

本文将介绍工单通知实现过程中的一些小技巧。所有演示均基于Django2.0

阅读此篇文章你可以:

  • 解锁一个python if的使用新姿势
  • 获取一个利用signals做通知的真实案例

背景说明

先看看工单表简化后的结构

  1. class Ticket(models.Model):
  2. '''工单表'''
  3. STATE = (
  4. (1, '待审批'),
  5. (2, '已撤销'),
  6. (3, '已通过'),
  7. (4, '被拒绝'),
  8. (5, '已挂起'),
  9. (6, '执行中'),
  10. (7, '已完成'),
  11. (8, '已失败')
  12. )
  13. create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  14. create_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name='创建用户')
  15. state = models.IntegerField(choices=STATE, default=1, verbose_name='工单状态')

Ticket工单表有一个state字段标识当前工单状态,这个状态会随着工单的进行而改变,每当工单状态改变时就需要发送通知给相应的用户,例如工单创建时,需要发送给创建者一个工单创建成功的通知,同时发送给审核者一个待审核的通知

通知

每一个状态的变化都需要通知,为了代码易读及解耦,我们需要写一个单独的通知类,当需要通知的时候调用一下就好了。通知类中需要判断当前工单的状态,那么通常会写成下边这样

  1. class Notify:
  2. def __init__(self):
  3. self.dba_list = ["dba1@ops-coffee.cn", "dba2@ops-coffee.cn"]
  4.  
  5. def migration(self, pk):
  6. '''迁移通知'''
  7.  
  8. _t = Ticket.objects.get(id=pk)
  9. _u = _t.create_user.username
  10. _s = _t.state
  11.  
  12. _d = "https://ops-coffee.cn/workflow/migration/%d/" % (_t.id)
  13.  
  14. if _s == 1:
  15. try:
  16. Email(
  17. subject="[已提交]-[overmind]数据迁移工单",
  18. content="你的数据迁移工单已提交,正在等待DBA审批,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" % _d,
  19. reciever_list=[_u]
  20. )
  21. except Exception as e:
  22. print('Error:' + str(e))
  23.  
  24. try:
  25. Email(
  26. subject="[待审批]-[overmind]数据迁移工单",
  27. content="你有工单需要审批,点击下方工单详情链接及时审批。\r\n\r\n工单详情:%s" % _d,
  28. reciever_list=self.dba_list
  29. )
  30. except Exception as e:
  31. print('Error:' + str(e))
  32. elif _s == 6:
  33. try:
  34. Email(
  35. subject="[执行中]-[overmind]数据迁移工单",
  36. content="数据迁移工单已通过DBA审核,正在执行中,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" % _d,
  37. reciever_list=[_u] + self.dba_list,
  38. )
  39. except Exception as e:
  40. print('Error:' + str(e))
  41. elif _s == 7:
  42. try:
  43. Email(
  44. subject="[已完成]-[overmind]数据迁移工单",
  45. content="数据迁移工单已自动完成迁移,请检查最终状态,如有任何疑问随时联系DBA。\r\n\r\n工单详情:%s" % _d,
  46. reciever_list=[_u] + self.dba_list,
  47. )
  48. except Exception as e:
  49. print('Error:' + str(e))

以上的代码可以看出来写了很多重复的try代码,并且当状态越多,if判断越复杂时重复的代码也会越来越多,有没有更简洁的方式呢?

看看下边的代码,维护一个状态字典,然后用一个if判断就可以实现上边一堆if的代码写法,是不是就简洁了很多

  1. class Notify:
  2. def __init__(self):
  3. self.dba_list = ["dba1@ops-coffee.cn", "dba2@ops-coffee.cn"]
  4.  
  5. def migration(self, pk):
  6. '''迁移通知'''
  7. _t = Ticket.objects.get(id=pk)
  8. _u = _t.create_user.username
  9. _s = _t.state
  10. _d = "https://ops-coffee.cn/workflow/migration/%d/" %(_t.id)
  11. smap = {
  12. 1: [{
  13. "subject": "[已提交]-[overmind]数据迁移工单",
  14. "content": "你的数据迁移工单已提交,正在等待DBA审批,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" %_d,
  15. "reciever_list": [_u],
  16. }, {
  17. "subject": "[待审批]-[overmind]数据迁移工单",
  18. "content": "你有工单需要审批,点击下方工单详情链接及时审批。\r\n\r\n工单详情:%s" %_d,
  19. "reciever_list": self.dba_list,
  20. }],
  21. 6: [{
  22. "subject": "[执行中]-[overmind]数据迁移工单",
  23. "content": "数据迁移工单已通过DBA审核,正在执行中,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" %_d,
  24. "reciever_list": [_u] + self.dba_list,
  25. }],
  26. 7: [{
  27. "subject": "[已完成]-[overmind]数据迁移工单",
  28. "content": "数据迁移工单已自动完成迁移,请检查最终状态,如有任何疑问随时联系DBA。\r\n\r\n工单详情:%s" %_d,
  29. "reciever_list": [_u] + self.dba_list,
  30. }]
  31. }
  32. _list = smap[_s]
  33. for i in range(0, len(_list)):
  34. try:
  35. Email(
  36. subject=_list[i]['subject'],
  37. content=_list[i]['content'],
  38. reciever_list=_list[i]['reciever_list']
  39. )
  40. except Exception as e:
  41. print('Error:' +str(e))

在构造字典的时候采用了状态做key,通知变量做value,同时一个状态可能会产生多个不同的通知,所以value采用列表的方式,这样即可轻松实现一个状态多条通知,每条通知都可以发给不同的人,有不同的主题,不同的内容。

Signals

上边我们已经写好了发送通知的类,在view里每次修改工单状态之后调用下通知类即可实现通知发送,但这样通知跟view强耦合,且通知会分散在view中的多个地方,造成代码冗余且不够优雅。我们需要一个简单优雅的方式来实现,signals可以说是非常有用了

Signals是Django自带的一个信号调度程序。如果你对svn或者git之类的hooks有了解,这个理解起来就简单多了,通俗来说就是当你的程序产生一个事件时,会通过signals自动触发其他的事件。就比如我们这个工单系统通知,当工单状态产生变化时自动发送邮件给相关人。

Django内部已经定义好了一些signal供我们使用,如果不能满足我们也可以自定义signal,其中Django内部定义的signal主要分为几类

  1. model signals
  2. pre_init
  3. post_init
  4. pre_save
  5. post_save
  6. pre_delete
  7. post_delete
  8. m2m_changed
  9. class_prepared
  10. management signals
  11. pre_migrate
  12. post_migrate
  13. request/response signals
  14. request_started
  15. request_finished
  16. got_request_exception
  17. test signals
  18. setting_changed
  19. template_rendered
  20. Database Wrappers
  21. connection_created :连接建立时触发

那么信号究竟该如何使用呢?下边一个实际的例子来说明下信号的使用

就以我们发送通知的需求为例,workflow是一个普通的app,第一步需要新建 workflow/signals.py 文件绑定signal

  1. from django.db.models import signals
  2. from django.dispatch import receiver
  3. from workflow.models import Ticket
  4. from workflow.backends.notify import Notify
  5. @receiver(signals.post_init, sender=Ticket)
  6. def migrate_notify_init(instance, **kwargs):
  7. instance.old_state = instance.state
  8. @receiver(signals.post_save, sender=Ticket)
  9. def migrate_notify_post(instance, created, **kwargs):
  10. if created or instance.old_state != instance.state:
  11. Notify().migration(instance.id)

这里用到了两个signal, post_init 和 post_save

在model初始化之后通过 post_init 信号获取到state的值作为初始状态值,在每次model执行save方法后调用 post_save 信号获取到新的状态值,对两次状态值做比较如果不一致则表示状态有更新发送通知

是上边的判断只能判断到状态变更了发通知,但工单在第一次创建时old_state和state是一样的,所以也需要在save之后判断下这次操作是不是新建,如果是新建同样需要发送通知

第二步加载signal,需要修改两个配置文件

  1. config1 workflow/apps.py
  2. from django.apps import AppConfig
  3. class WorkflowConfig(AppConfig):
  4. name = 'workflow'
  5. def ready(self):
  6. import workflow.signals
  7. config2 workflow/__init__.py
  8. default_app_config = 'workflow.apps.WorkflowConfig'

绑定成功后就可以在每次工单状态发生变化时发送邮件了

总结

以上所述是小编给大家介绍的Django使用Signals监测model字段变化发送通知的一些技巧,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对w3xue网站的支持!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号