临时禁用 auto_now / auto_now_add

138 投票
15 回答
40720 浏览
提问于 2025-04-17 02:45

我有一个这样的模型:

class FooBar(models.Model):
    createtime = models.DateTimeField(auto_now_add=True)
    lastupdatetime = models.DateTimeField(auto_now=True)

我想要为一些模型实例重写这两个日期字段(在迁移数据时使用)。目前的解决方案是这样的:

for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = False
    elif field.name == "createtime":
        field.auto_now_add = False

new_entry.createtime = date
new_entry.lastupdatetime = date
new_entry.save()

for field in new_entry._meta.local_fields:
    if field.name == "lastupdatetime":
        field.auto_now = True
    elif field.name == "createtime":
        field.auto_now_add = True

有没有更好的解决办法呢?

15 个回答

55

你还可以使用 save() 方法中的 update_fields 参数,来传入你的 auto_now 字段。下面是一个例子:

# Date you want to force
new_created_date = date(year=2019, month=1, day=1)

# The `created` field is `auto_now` in your model
instance.created = new_created_date
instance.save(update_fields=['created'])

这是来自Django文档的解释: https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save

61

你不能用其他方式来禁用 auto_nowauto_now_add,只能按照你现在的做法。如果你需要更灵活地修改这些值,使用 auto_nowauto_now_add 可能不是最好的选择。通常,使用 default 或者重写 save() 方法,在对象保存之前进行一些处理,会更灵活。

通过使用 default 和重写的 save() 方法,你可以这样定义你的模型来解决问题:

class FooBar(models.Model):
    createtime = models.DateTimeField(default=datetime.datetime.now)
    lastupdatetime = models.DateTimeField()

    def save(self, *args, **kwargs):
        if not kwargs.pop('skip_lastupdatetime', False):
            self.lastupdatetime = datetime.datetime.now()

        super(FooBar, self).save(*args, **kwargs)

在你的代码中,如果你想跳过自动更新时间的更改,只需使用

new_entry.save(skip_lastupdatetime=True)

如果你的对象是在管理界面或其他地方保存的,save() 方法会在没有 skip_lastupdatetime 参数的情况下被调用,这样它的行为就和之前使用 auto_now 一样。

138

我最近在测试我的应用时遇到了这种情况。我需要“强制”使用一个过期的时间戳。对我来说,我是通过更新查询集来实现这个目的的。像这样:

# my model
class FooBar(models.Model):
    title = models.CharField(max_length=255)
    updated_at = models.DateTimeField(auto_now=True, auto_now_add=True)


# my tests
foo = FooBar.objects.get(pk=1)
    
# force a timestamp
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
FooBar.objects.filter(pk=foo.pk).update(updated_at=lastweek)

# do the testing.

撰写回答