上一篇文章《运维效率之数据迁移自动化》中讲到了工单通知,本文将介绍工单通知实现过程中的一些小技巧。所有演示均基于Django2.0
阅读此篇文章你可以:
解锁一个python if的使用新姿势
获取一个利用signals做通知的真实案例
背景说明先看看工单表简化后的结构
class Ticket(models.Model): '''工单表''' STATE = ( (1, '待审批'), (2, '已撤销'), (3, '已通过'), (4, '被拒绝'), (5, '已挂起'), (6, '执行中'), (7, '已完成'), (8, '已失败') ) create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') create_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name='创建用户') state = models.IntegerField(choices=STATE, default=1, verbose_name='工单状态')Ticket工单表有一个state字段标识当前工单状态,这个状态会随着工单的进行而改变,每当工单状态改变时就需要发送通知给相应的用户,例如工单创建时,需要发送给创建者一个工单创建成功的通知,同时发送给审核者一个待审核的通知
通知每一个状态的变化都需要通知,为了代码易读及解耦,我们需要写一个单独的通知类,当需要通知的时候调用一下就好了。通知类中需要判断当前工单的状态,那么通常会写成下边这样
class Notify: def __init__(self): self.dba_list = ["dba1@ops-coffee.cn", "dba2@ops-coffee.cn"] def migration(self, pk): '''迁移通知''' _t = Ticket.objects.get(id=pk) _u = _t.create_user.username _s = _t.state _d = "https://ops-coffee.cn/workflow/migration/%d/" % (_t.id) if _s == 1: try: Email( subject="[已提交]-[overmind]数据迁移工单", content="你的数据迁移工单已提交,正在等待DBA审批,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" % _d, reciever_list=[_u] ) except Exception as e: print('Error:' + str(e)) try: Email( subject="[待审批]-[overmind]数据迁移工单", content="你有工单需要审批,点击下方工单详情链接及时审批。\r\n\r\n工单详情:%s" % _d, reciever_list=self.dba_list ) except Exception as e: print('Error:' + str(e)) elif _s == 6: try: Email( subject="[执行中]-[overmind]数据迁移工单", content="数据迁移工单已通过DBA审核,正在执行中,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" % _d, reciever_list=[_u] + self.dba_list, ) except Exception as e: print('Error:' + str(e)) elif _s == 7: try: Email( subject="[已完成]-[overmind]数据迁移工单", content="数据迁移工单已自动完成迁移,请检查最终状态,如有任何疑问随时联系DBA。\r\n\r\n工单详情:%s" % _d, reciever_list=[_u] + self.dba_list, ) except Exception as e: print('Error:' + str(e))以上的代码可以看出来写了很多重复的try代码,并且当状态越多,if判断越复杂时重复的代码也会越来越多,有没有更简洁的方式呢?
看看下边的代码,维护一个状态字典,然后用一个if判断就可以实现上边一堆if的代码写法,是不是就简洁了很多
class Notify: def __init__(self): self.dba_list = ["dba1@ops-coffee.cn", "dba2@ops-coffee.cn"] def migration(self, pk): '''迁移通知''' _t = Ticket.objects.get(id=pk) _u = _t.create_user.username _s = _t.state _d = "https://ops-coffee.cn/workflow/migration/%d/" %(_t.id) smap = { 1: [{ "subject": "[已提交]-[overmind]数据迁移工单", "content": "你的数据迁移工单已提交,正在等待DBA审批,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" %_d, "reciever_list": [_u], }, { "subject": "[待审批]-[overmind]数据迁移工单", "content": "你有工单需要审批,点击下方工单详情链接及时审批。\r\n\r\n工单详情:%s" %_d, "reciever_list": self.dba_list, }], 6: [{ "subject": "[执行中]-[overmind]数据迁移工单", "content": "数据迁移工单已通过DBA审核,正在执行中,后续有状态变更将会自动通知你。\r\n\r\n工单详情:%s" %_d, "reciever_list": [_u] + self.dba_list, }], 7: [{ "subject": "[已完成]-[overmind]数据迁移工单", "content": "数据迁移工单已自动完成迁移,请检查最终状态,如有任何疑问随时联系DBA。\r\n\r\n工单详情:%s" %_d, "reciever_list": [_u] + self.dba_list, }] } _list = smap[_s] for i in range(0, len(_list)): try: Email( subject=_list[i]['subject'], content=_list[i]['content'], reciever_list=_list[i]['reciever_list'] ) except Exception as e: print('Error:' +str(e))在构造字典的时候采用了状态做key,通知变量做value,同时一个状态可能会产生多个不同的通知,所以value采用列表的方式,这样即可轻松实现一个状态多条通知,每条通知都可以发给不同的人,有不同的主题,不同的内容。
Signals上边我们已经写好了发送通知的类,在view里每次修改工单状态之后调用下通知类即可实现通知发送,但这样通知跟view强耦合,且通知会分散在view中的多个地方,造成代码冗余且不够优雅。我们需要一个简单优雅的方式来实现,signals可以说是非常有用了