You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 04:20:15