Django 3.2 post_save信号异常:实例触发范围与Apache重复保存问题
Django post_save信号相关问题及密码历史修复方案
基础问题解答
问题1:即使代码中仅保存单个实例,Django 3.2的post_save信号是否会针对所有模型实例被触发?
答:不会。post_save信号仅针对当前执行save()操作的具体实例触发,sender参数指定了监听的模型,只有该模型的实例完成保存动作时,信号才会触发,且仅传递当前被保存的instance对象。问题2:在Django Model Admin点击保存按钮时,post_save信号是否会针对所有Mailbox模型实例被调用?
答:不会。Admin中点击保存只会触发当前被编辑/创建的单个Mailbox实例的post_save信号,不会遍历或触发模型的所有实例。问题3:post_save信号在Apache服务器中是否会持续运行?
答:不会。post_save信号是请求生命周期内的触发式操作,仅当Django应用处理到模型save()操作时才会触发对应的信号函数,请求结束后流程即终止,与服务器类型(Apache/Nginx)无关。
当前业务问题
在Apache环境下,刷新页面会导致相同密码被重复保存到下一个密码列中,需要实现检查最近5次密码历史的逻辑,避免重复存储相同密码。
现有代码
密码历史检查函数
def check_oldest_password_instance(mbox): passwd_list = [] oldest_password = None oldest_password_datetime = datetime.now() pfield = '1' n = 0 mphistory = Phistory.objects.filter(mbox=mbox) if mphistory: print('======phistory exists====', mphistory) for p in mphistory: if p.password1: passwd_list.append(p.password1) oldest_password_datetime = p.datetime1 oldest_password = p.password1 pfield = '1' if p.password2: passwd_list.append(p.password2) if oldest_password_datetime > p.datetime2: oldest_password_datetime = p.datetime2 oldest_password = p.password2 pfield = '2' if p.password3: passwd_list.append(p.password3) if oldest_password_datetime > p.datetime3: oldest_password_datetime = p.datetime3 oldest_password = p.password3 pfield = '3' if p.password4: passwd_list.append(p.password4) if oldest_password_datetime > p.datetime4: oldest_password_datetime = p.datetime4 oldest_password = p.password4 pfield = '4' if p.password5: passwd_list.append(p.password5) if oldest_password_datetime > p.datetime5: oldest_password_datetime = p.datetime5 oldest_password = p.password5 pfield = '5' print(len(passwd_list),pfield,'passwd_list_len,pfield_oldest') n = len(passwd_list) # For new mailbox, check if all 5 values are populated # if n == 0: # pfield = '1' if n == 1: pfield = '2' if n == 2: pfield = '3' if n == 3: pfield = '4' if n == 4: pfield = '5' print(pfield,n, 'pfield-n------------------') else: oldest_password = None n = 0 pfield = '1' return (pfield, passwd_list)
post_save信号处理函数
def mbox_post_save(sender, instance, created, **kwargs): mphistory = None new_phistory = None mphistory = None print('post save callllll') if created: # Store the hashed password in Phistory table #---------------- new_phistory = Phistory() new_phistory.mbox = instance new_phistory.password1 = instance.mpassword new_phistory.datetime1 = datetime.now() new_phistory.save() #---------------- else: print('# edit mbox post_save') # Store the hashed password in Phistory table #---------------- try: mphistory = Phistory.objects.get(mbox=instance) print(mphistory,'mppppp=======================') except Exception as e: print(e) if mphistory: print('PHISTORY--------') (oldest_pfield, passwd_list) = check_oldest_password_instance(instance) if oldest_pfield == '1': mphistory.password1 = instance.mpassword mphistory.datetime1 = datetime.now() elif oldest_pfield == '2': mphistory.password2 = instance.mpassword mphistory.datetime2 = datetime.now() elif oldest_pfield == '3': mphistory.password3 = instance.mpassword mphistory.datetime3 = datetime.now() elif oldest_pfield == '4': mphistory.password4 = instance.mpassword mphistory.datetime4 = datetime.now() elif oldest_pfield == '5': mphistory.password5 = instance.mpassword mphistory.datetime5 = datetime.now() mphistory.save() else: if not mphistory: print('# Add new phistory object 1st time for existing mbox') new_phistory = Phistory() new_phistory.mbox = instance new_phistory.password1 = instance.mpassword new_phistory.datetime1 = datetime.now() new_phistory.save() #---------------- return
信号连接代码
post_save.connect(mbox_post_save, sender=Mbox)
修复方案
1. 添加重复密码检查逻辑
在mbox_post_save函数的编辑分支中,新增密码重复校验,若当前密码已在历史记录中则跳过保存:
else: print('# edit mbox post_save') # Store the hashed password in Phistory table #---------------- try: mphistory = Phistory.objects.get(mbox=instance) print(mphistory,'mppppp=======================') except Exception as e: print(e) if mphistory: print('PHISTORY--------') (oldest_pfield, passwd_list) = check_oldest_password_instance(instance) # 新增:检查当前密码是否在历史列表中,存在则直接返回 current_password = instance.mpassword if current_password in passwd_list: print('当前密码已在历史记录中,跳过保存') return if oldest_pfield == '1': mphistory.password1 = current_password mphistory.datetime1 = datetime.now() elif oldest_pfield == '2': mphistory.password2 = current_password mphistory.datetime2 = datetime.now() elif oldest_pfield == '3': mphistory.password3 = current_password mphistory.datetime3 = datetime.now() elif oldest_pfield == '4': mphistory.password4 = current_password mphistory.datetime4 = datetime.now() elif oldest_pfield == '5': mphistory.password5 = current_password mphistory.datetime5 = datetime.now() mphistory.save() else: if not mphistory: print('# Add new phistory object 1st time for existing mbox') new_phistory = Phistory() new_phistory.mbox = instance new_phistory.password1 = instance.mpassword new_phistory.datetime1 = datetime.now() new_phistory.save() #----------------
2. 修正历史密码查找逻辑
原check_oldest_password_instance函数存在遍历覆盖问题,调整为一对一场景下的正确逻辑:
def check_oldest_password_instance(mbox): passwd_list = [] oldest_password_datetime = datetime.max # 初始化为最大时间,确保能找到最早记录 pfield = '1' n = 0 try: mphistory = Phistory.objects.get(mbox=mbox) except Phistory.DoesNotExist: return (pfield, passwd_list) print('======phistory exists====', mphistory) # 逐个检查密码字段,收集历史并定位最早记录 if mphistory.password1: passwd_list.append(mphistory.password1) if mphistory.datetime1 < oldest_password_datetime: oldest_password_datetime = mphistory.datetime1 pfield = '1' if mphistory.password2: passwd_list.append(mphistory.password2) if mphistory.datetime2 < oldest_password_datetime: oldest_password_datetime = mphistory.datetime2 pfield = '2' if mphistory.password3: passwd_list.append(mphistory.password3) if mphistory.datetime3 < oldest_password_datetime: oldest_password_datetime = mphistory.datetime3 pfield = '3' if mphistory.password4: passwd_list.append(mphistory.password4) if mphistory.datetime4 < oldest_password_datetime: oldest_password_datetime = mphistory.datetime4 pfield = '4' if mphistory.password5: passwd_list.append(mphistory.password5) if mphistory.datetime5 < oldest_password_datetime: oldest_password_datetime = mphistory.datetime5 pfield = '5' print(len(passwd_list), pfield, 'passwd_list_len,pfield_oldest') n = len(passwd_list) # 根据已有密码数量确定下一个更新字段 if n == 0: pfield = '1' elif n == 1: pfield = '2' elif n == 2: pfield = '3' elif n == 3: pfield = '4' elif n == 4: pfield = '5' # 已有5个密码时,覆盖最早的那个字段 print(pfield, n, 'pfield-n------------------') return (pfield, passwd_list)
上述修改后,既解决了重复密码保存的问题,也修正了原函数中查找最早密码记录的逻辑错误。
内容的提问来源于stack exchange,提问作者user956424
相关产品推荐
相关产品推荐

