应用背景:
近期在做一个小应用,使用的是Django框架,其中有一个需求是要对数据中每个字段的修改、变更进行记录。
为什么要这么做?主要应用的场景是对于不分表单有“审核”流程,审核结果有通过、不通过;即使对于通过的数据, 后续的审核流程中,仍然有更改审核结果的权限,因此必须记录每个人对每个字段的修改内容,便于做追溯。
具体做法:
针对这个需求,在网上也查了很多资料,但是好像都没有比较完善系统的说法,于是看着Django官方文档,结合众多网友的的方案做尝试。主要步骤:
- 初步选定重写 save_model 函数来实现保存的时候做好记录;
- 新建一个model,用于记录数据变更结果;(应用MySql数据库)
- 在save_model中获取表单数据;
- 再保存表单时,将数据写入表单。
相关代码:
虽然可以使用日志作为数据记录,但是日志一般是文本格式,结构性不强,不便于后期的查询处理。所以还是选择使用数据库进行记录;同时日志也使用了;(至于速度嘛……都用python了,还考虑啥访问速度……)
数据记录表:
# models.py
# 简单日志记录,用于主要记录节点信息
class RzLog(models.Model):
guid = models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False)
sheet = models.CharField('表单名称', max_length=100, default='')
user = models.CharField('操作人员', max_length=100, default='')
the_id = models.CharField('数据ID', max_length=255, default='')
the_key = models.CharField('字段名称', max_length=255, default='')
old_values = models.CharField('原数据', max_length=255, default='')
new_values = models.CharField('新数据', max_length=255, default='')
log_date = models.DateTimeField('修改时间', default=choice_list.date_time, null=True)
note = models.TextField('备注信息', null=True, default='', blank=True)
create_user = models.ForeignKey(User, related_name='create_user_rz', on_delete=models.CASCADE, db_constraint=False, verbose_name='创建人员', default=1000)
update_user = models.ForeignKey(User, related_name='update_user_rz', on_delete=models.CASCADE, db_constraint=False, verbose_name='修改人员', default=1000)
create_date = models.DateTimeField('创建时间', auto_now_add=True)
update_date = models.DateTimeField('更新时间', auto_now=True)
admin中重写save_model 代码,其中通过form.has_changed()来获取变更字段的数据。以下是相关代码,代码中使用了大量的日志记录,这个是初期调试的时候,一步步尝试的时候应用的,后期正式应用可以去掉,为了让大家能更清晰地应用,这里先保留了所以记录和说明。
# admin.py
def save_model(self, request, obj, form, change):
obj.update_user = request.user
obj.receiver = request.user.username
# 用于记录数据变化信息
if form.has_changed():
change_list = form.changed_data
logger.info(f'表格数据变化点:{change_list}')
try:
changed_dict = form.cleaned_data
for i in change_list:
x = YiJieShou.objects.values_list(i, flat=True).filter(id=obj.id) # 原数据库保存的数据
y = changed_dict[i] # 当前新写入的数据
# ----------------------------------
# 如果有特殊字段需要另外的动作进行处理的,使用下面的部分代码
# 当变更审批状态的时候
if i == 'is_jieshou': # 此处为特别关注的字段,针对此字段的变动如果需要有其他的操作,可以在此处进行设置相关动作
obj.jieshou_date = datetime.datetime.now()
if not y:
pass # 设定特殊动作
else:
pass
elif i == 'is_doubt':
if y == 1:
pass # 设定特殊动作
else:
pass # 设定特殊动作
else:
pass # 设定特殊动作
# ---------------------------------------------------------
# 如果不需要特殊字段的处理,跳过 -- 之间的不分代码即可
# 将相关数据的变化,写入数据库进行存储
RzLog.objects.create(sheet='PassJieShou',user=request.user.username,the_id=str(obj),the_key=i,old_values=str(x[0]),new_values=str(y),log_date=datetime.datetime.now(),create_user=request.user)
except BaseException as e:
logger.error(f'错误类型是:{e.__class__.__name__}\n错误原因: {e}')
logger.error(f'详细错误信息a:{traceback.format_exc()}')
super().save_model(request, obj, form, change)
上面代码中的x 、y分别代表的是,x原来表单中保存的数据, y本次新写入的{修改的)数据;
如果只需要简单的记录数据的变化,可以直接由x\y,直接跳转RzLog.objects.create()部分。中间部分是针对一些特殊字段,设定特殊的动作用的。