临时禁用 auto_now / auto_now_add
我有一个这样的模型:
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_now
或 auto_now_add
,只能按照你现在的做法。如果你需要更灵活地修改这些值,使用 auto_now
和 auto_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.