Django pre_delete信号中update查询语句不生效问题
问题原因
该update语句本身可以正常执行,最终未达预期是Django模型删除的固定执行顺序导致的:
- 调用
Wallet实例的delete()方法时,Django会优先完成级联关联对象收集:将所有外键指向当前钱包、且on_delete为CASCADE(Django外键默认配置)的Transaction对象全部加入内存中的待删除列表,该步骤在所有pre_delete信号触发前就已执行完毕。 - 待删除列表收集完成后,Django才会触发
Wallet的pre_delete信号,执行你编写的接收器逻辑。此时执行的update语句确实会将关联交易的外键修改为目标默认钱包,但内存中已经生成的待删除列表不会随这个更新动态调整。 - 信号逻辑执行结束后,Django会按照预先收集的待删除列表,将所有关联
Transaction批量删除,最后删除当前Wallet实例。
最终查询数据库时,关联交易已经被级联删除,无法看到钱包字段更新后的结果,表现为update语句未生效。
修复方案
可根据业务场景选择以下任意一种方案:
- 推荐方案:业务层先迁移数据再执行删除
不要依赖pre_delete信号处理关联数据迁移,将交易迁移逻辑前置到钱包删除的业务代码中,在数据库事务内先完成关联交易的钱包更新,再执行钱包删除操作:from django.db import transaction with transaction.atomic(): # 原有校验逻辑 if wallet.is_default: raise ValidationError({"is_default": "You can't delete the default wallet"}) q = {"company": wallet.company} if wallet.company else {"user": wallet.user} try: default_wallet = Wallet.objects.get(**q, is_default=True) except Wallet.DoesNotExist: default_wallet = Wallet.objects.filter(**q).exclude(pk=wallet.pk).first() if not default_wallet: raise ValidationError({"is_default": "You can't delete the last wallet"}) # 先迁移关联交易,此时未触发删除收集逻辑,交易不会被级联删除 wallet.transactions.update(wallet=default_wallet) # 最后执行钱包删除 wallet.delete() - 方案二:重写Wallet模型的delete方法
如果不想将逻辑散落在各个业务调用点,可以将迁移逻辑封装到模型的delete方法中,在调用父类删除逻辑前完成交易迁移:class Wallet(models.Model): # 原有字段定义 def delete(self, *args, **kwargs): if self.is_default: raise ValidationError({"is_default": "You can't delete the default wallet"}) q = {"company": self.company} if self.company else {"user": self.user} try: default_wallet = Wallet.objects.get(**q, is_default=True) except Wallet.DoesNotExist: default_wallet = Wallet.objects.filter(**q).exclude(pk=self.pk).first() if not default_wallet: raise ValidationError({"is_default": "You can't delete the last wallet"}) # 先迁移关联交易 self.transactions.update(wallet=default_wallet) # 执行原有删除逻辑 super().delete(*args, **kwargs) - 方案三:调整外键on_delete规则
如果必须保留信号中的处理逻辑,可以将Transaction模型关联Wallet的外键on_delete参数设置为models.DO_NOTHING,阻止Django默认的级联删除行为。注意该方案需要确保所有钱包删除路径都覆盖了交易迁移逻辑,否则会产生指向不存在钱包的脏数据。
内容的提问来源于stack exchange,提问作者MohammadReza
相关产品推荐
相关产品推荐

