如何在单元测试中覆盖Django设置auto_now_add=True的DateTimeField值
Django 测试中覆盖
auto_now_add 字段赋值的方法 auto_now_add=True 的字段本质不会限制手动赋值,Django 仅会在模型实例首次入库时默认填充当前时间,你可以根据使用场景选择以下方案覆盖默认值:
方案1:原生ORM直接传值
不需要修改模型定义,创建实例时显式传入字段值即可,优先级高于自动填充逻辑:
from django.utils import timezone from .models import ABC # 直接指定时间创建实例 abc = ABC.objects.create(sent_at=timezone.now() - timezone.timedelta(seconds=40)) # 输出的是你指定的时间,不会被自动覆盖 print(abc.sent_at)
方案2:适配 factory_boy 工厂类的写法
你当前写的工厂类直接赋值的写法存在缺陷:时间会在工厂类定义时计算,所有实例复用同一个固定时间,且部分场景可能被自动填充逻辑覆盖,调整为以下两种写法即可:
写法A:LazyAttribute 动态计算时间
确保每次创建实例时才计算时间,满足绝大多数场景需求:
import factory from django.utils import timezone from .models import ABC class ABCFactory(factory.django.DjangoModelFactory): class Meta: model = ABC sent_at = factory.LazyAttribute(lambda o: timezone.now() - timezone.timedelta(seconds=40))
写法B:post_generation 钩子强制赋值
如果写法A仍然被自动填充逻辑覆盖,用后置钩子强制更新字段:
import factory from django.utils import timezone from .models import ABC class ABCFactory(factory.django.DjangoModelFactory): class Meta: model = ABC @factory.post_generation def force_sent_at(self, create, extracted, **kwargs): # 仅处理入库的实例,跳过build模式 if not create: return # 支持自定义传值,没有传值则用默认规则 self.sent_at = extracted if extracted else timezone.now() - timezone.timedelta(seconds=40) self.save(update_fields=["sent_at"]) # 使用示例 abc1 = ABCFactory() # 默认使用当前时间减40秒 abc2 = ABCFactory(force_sent_at=timezone.now() - timezone.timedelta(days=2)) # 自定义时间
特殊场景:临时禁用自动填充逻辑
如果有其他逻辑导致赋值不生效,可以在测试用例中临时修改字段属性,用完后还原避免影响其他测试:
def test_custom_sent_at(): # 临时关闭auto_now_add逻辑 sent_at_field = ABC._meta.get_field("sent_at") origin_value = sent_at_field.auto_now_add sent_at_field.auto_now_add = False # 测试逻辑 target_time = timezone.now() - timezone.timedelta(seconds=40) abc = ABC.objects.create(sent_at=target_time) assert abc.sent_at == target_time # 还原字段属性 sent_at_field.auto_now_add = origin_value
内容的提问来源于stack exchange,提问作者Muhammad Shehzad
相关产品推荐
相关产品推荐

