Django pre_save信号修改Model后测试实例值未更新问题
问题根因
- 数据库层面的字段修改其实已经持久化成功了,问题出在Django ORM的实例缓存机制:测试用例中持有的
self.soon_to_be_obsolete_tag是信号触发前就加载到内存的模型实例,你在信号逻辑里是重新查库拿到了一个新的Tag实例做修改保存,两个实例是独立的内存对象,ORM不会自动把新实例的变更同步到已经存在的旧内存实例上,所以你直接读旧实例的属性,拿到的还是初始加载时的False值。 - 单步调试时值符合预期,是因为你在信号逻辑里查看的是刚查出来、修改过的新Tag实例,不是测试里持有的那个旧实例,不存在修改回滚的问题。
- 额外注意:你当前Tag模型的字段写法存在错误,
is_obsolete = False是普通类属性,不是Django模型字段,正常情况下这个字段不会被同步到数据库表结构中,需要修正为models.BooleanField类型。
修复方案
- 测试用例中在断言前手动刷新内存实例,从数据库拉取最新值,这是最符合测试逻辑的写法——直接验证数据库中的持久化结果是否符合预期:
def test_status_set_tag_info_obsolete(self): """ check to see if the creation of a statusInfo object which runs a pre_save signal changes the soon_to_be_obsolete_tag to is_obsolete=True """ self.assertFalse(self.soon_to_be_obsolete_tag.is_obsolete) self.main_status_info = StatusInfoFactory( affected_tag=self.soon_to_be_obsolete_tag, status="o", # obsolete by=self.new_replacement_tag, ) # 从数据库重载最新字段值 self.soon_to_be_obsolete_tag.refresh_from_db() self.assertTrue(self.soon_to_be_obsolete_tag.is_obsolete)
- 修正Tag模型的字段定义,避免字段不存在导致的写入异常:
class Tag(models.Model): # 其余字段定义 is_obsolete = models.BooleanField(default=False) is_deprecated = models.BooleanField(default=False)
- 可选优化:信号逻辑里不需要额外查库,可以直接操作传入实例关联的affected_tag对象,减少一次数据库查询,同时因为是同引用操作,内存中持有的实例也会同步更新:
def inner_tag_status_change(instance, **kwargs): affected_tag = instance.affected_tag for key, value in kwargs.items(): setattr(affected_tag, key, value) affected_tag.save(update_fields=list(kwargs.keys()))
注意:即使做了这个优化,测试中依然建议显式调用
refresh_from_db(),避免出现仅修改了内存值、数据库实际未写入的假阳性测试结果。
内容的提问来源于stack exchange,提问作者Back2Basics
相关产品推荐
相关产品推荐

