暂时禁用auto_now / auto_now_add
我有一个这样的模型:
class FooBar(models.Model): createtime = models.DateTimeField(auto_now_add=True) lastupdatetime = models.DateTimeField(auto_now=True)
我想覆盖一些模型实例的两个date字段(在迁移数据时使用)。 目前的解决scheme如下所示:
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
有更好的解决scheme吗?
我最近在testing我的应用程序时遇到了这种情况。 我需要“强制”过期的时间戳。 在我的情况下,我通过使用查询集更新做了伎俩。 喜欢这个:
# 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.
你不能以另一种方式真正地禁用auto_now / auto_now_add。 如果您需要灵活地更改这些值, auto_now
/ auto_now_add
不是最佳select。 使用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)
在你的代码中,你想跳过自动lastupdatetime更改,只需使用
new_entry.save(skip_lastupdatetime=True)
如果您的对象保存在pipe理界面或其他地方,save()将被调用,而不会使用skip_lastupdatetime参数,并且它的行为将与之前的auto_now
。
我使用了提问者的build议,并创build了一些function。 这是用例:
turn_off_auto_now(FooBar, "lastupdatetime") turn_off_auto_now_add(FooBar, "createtime") new_entry.createtime = date new_entry.lastupdatetime = date new_entry.save()
这是实现:
def turn_off_auto_now(ModelClass, field_name): def auto_now_off(field): field.auto_now = False do_to_model(ModelClass, field_name, auto_now_off) def turn_off_auto_now_add(ModelClass, field_name): def auto_now_add_off(field): field.auto_now_add = False do_to_model(ModelClass, field_name, auto_now_add_off) def do_to_model(ModelClass, field_name, func): field = ModelClass._meta.get_field_by_name(field_name)[0] func(field)
可以创build类似的function来重新打开它们。
我使用上下文pipe理器来重用。
@contextlib.contextmanager def suppress_autotime(model, fields): _original_values = {} for field in model._meta.local_fields: if field.name in fields: _original_values[field.name] = { 'auto_now': field.auto_now, 'auto_now_add': field.auto_now_add, } field.auto_now = False field.auto_now_add = False try: yield finally: for field in model._meta.local_fields: if field.name in fields: field.auto_now = _original_values[field.name]['auto_now'] field.auto_now_add = _original_values[field.name]['auto_now_add']
像这样使用:
with suppress_autotime(my_object, ['updated']): my_object.some_field = some_value my_object.save()
繁荣。
对于那些在编写testing时看到这个问题的人来说,有一个名为freezegun的python库允许你伪造时间 – 所以当auto_now_add
代码运行时,它会得到你真正想要的时间。 所以:
from datetime import datetime, timedelta from freezegun import freeze_time with freeze_time('2016-10-10'): new_entry = FooBar.objects.create(...) with freeze_time('2016-10-17'): # use new_entry as you wish, as though it was created 7 days ago
它也可以用作装饰器 – 请参阅上面的链接以获得基本的文档。
我需要在迁移期间禁用一个DateTime字段的auto_now,并能够做到这一点。
events = Events.objects.all() for event in events: for field in event._meta.fields: if field.name == 'created_date': field.auto_now = False event.save()
如果您知道要将其限制为哪些字段并排除auto_now / auto_now_add字段,则还可以使用update_fields
参数来save()
:
https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save
我迟到了,但与其他几个答案类似,这是我在数据库迁移过程中使用的解决scheme。 与其他答案的区别在于,假设没有理由有多个这样的字段,这将禁用模型的所有 auto_now字段。
def disable_auto_now_fields(*models): """Turns off the auto_now and auto_now_add attributes on a Model's fields, so that an instance of the Model can be saved with a custom value. """ for model in models: for field in model._meta.local_fields: if hasattr(field, 'auto_now'): field.auto_now = False if hasattr(field, 'auto_now_add'): field.auto_now_add = False
然后使用它,你可以简单地做:
disable_auto_now_fields(Document, Event, ...)
而且它会通过所有您auto_now
的模型类并为您的所有auto_now
和auto_now_add
字段auto_now
核弹。
Django副本- Models.DateTimeField – dynamic更改auto_now_add值
那么今天下午我花了一些时间找出来,第一个问题是如何获取模型对象和代码。 我在serializer.py中restframework,例如在__init__
的序列化它不能有模型呢。 现在在to_internal_value中,你可以得到模型类,得到字段后修改字段属性,如下例所示:
class ProblemSerializer(serializers.ModelSerializer): def to_internal_value(self, data): ModelClass = self.Meta.model dfil = ModelClass._meta.get_field('date_update') dfil.auto_now = False dfil.editable = True