Django 暂时禁用“立即自动”/“立即自动添加”

Django 暂时禁用“立即自动”/“立即自动添加”,django,datetime,Django,Datetime,我有一个这样的模型: 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.

我有一个这样的模型:

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
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)
我想覆盖某些模型实例的两个日期字段(在迁移数据时使用)。当前的解决方案如下所示:

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
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)

有更好的解决方案吗?

您不能以其他方式真正禁用auto\u now\u添加。如果您需要灵活地更改这些值,
auto\u now
/
auto\u now\u add
不是最佳选择。在保存对象之前,使用
default
和/或重写
save()
方法进行操作通常更为灵活

使用
default
和重写的
save()
方法,解决问题的一种方法是如下定义模型:

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
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)

如果您的对象保存在管理界面或其他位置,则将在不使用skip_lastupdatetime参数的情况下调用save(),并且它的行为将与以前使用
auto_now

时的行为相同。我使用了询问者提出的建议,并创建了一些函数。以下是用例:

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)

可以创建类似的函数来重新打开它们。

我最近在测试应用程序时遇到了这种情况。我需要“强制”一个过期的时间戳。在我的例子中,我通过使用queryset更新实现了这个技巧。像这样:

# 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.

我需要在迁移过程中禁用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()

我来晚了,但与其他几个答案类似,这是我在数据库迁移期间使用的解决方案。与其他答案不同的是,在假设没有理由拥有多个这样的字段的情况下,这会禁用模型的所有自动字段

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\u now
auto\u now\u添加
字段。

为了可重用性,我采用了上下文管理器的方式

@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()

Boom.

对于那些在编写测试时看到这一点的人来说,有一个名为python的库,它允许你伪造时间-因此当
auto\u now\u 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

它还可以用作装饰器-请参阅上面的链接以了解基本文档。

您还可以使用
save()
update\u fields
参数,并立即传递
auto\u
字段。下面是一个例子:

# 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文档中的解释:

副本

好的,我花了今天下午的时间来发现,第一个问题是如何获取模型对象以及代码中的位置。我在serializer.py中的restframework中,例如在serializer的
\uuuu init\uuuu
中,它可能还没有模型。现在,在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

我需要的解决方案将与
update\u或\u create
一起使用,我是基于@andreaspelme代码找到这个解决方案的

唯一的变化是,您可以通过将修改字段设置为
skip
来设置跳过,而不仅仅是通过实际传递kwarg
skip_modified_update
to save()方法

只需
yourmodelobject.modified='skip'
,更新将被跳过

from django.db import models
from django.utils import timezone


class TimeTrackableAbstractModel(models.Model):
    created = models.DateTimeField(default=timezone.now, db_index=True)
    modified = models.DateTimeField(default=timezone.now, db_index=True)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        skip_modified_update = kwargs.pop('skip_modified_update', False)
        if skip_modified_update or self.modified == 'skip':
            self.modified = models.F('modified')
        else:
            self.modified = timezone.now()
        super(TimeTrackableAbstractModel, self).save(*args, **kwargs)

无需特殊代码,即可覆盖
auto\u now\u add

我在尝试创建具有特定日期的对象时遇到了这个问题:

Post.objects.create(publication_date=date, ...)
其中
publication\u date=models.DateField(auto\u now\u add=True)

这就是我所做的:

post = Post.objects.create(...)
post.publication_date = date
post.save()
这已成功地覆盖了
auto\u now\u add


作为一个更长期的解决方案,重写
save
方法是一个好办法:从

你甚至可以在工厂里使用它(工厂男孩)

来自django

DateField.auto\u现在\u添加 首次创建对象时自动将字段设置为“现在”。用于创建时间戳。请注意,始终使用当前日期您可以覆盖的不仅仅是默认值。因此,即使在创建对象时为此字段设置了一个值,它也将被忽略如果您希望能够修改此字段,请设置以下内容,而不是
auto\u now\u add=True

对于日期字段:
default=date.today
-from datetime.date.today()


对于DateTimeField:
default=timezone.now
-来自django.utils.timezone.now()

new\u entry.createtime.auto\u now=False?+1-这对于testing@akonsu否:“datetime.datetime”对象没有“auto\u now”属性。值得指出的是,不止几个核心开发人员有新的\u条目。\u meta.get\u字段(“date\u update”)更直接谢谢你的回答。下面是update()的文档:实际上,如果您不介意访问数据库,这个方法工作得很好。最后,我在测试中也使用了这个方法。Django文档:注意update()方法直接转换为SQL语句。这是直接更新的批量操作。它不会在模型上运行任何save()方法,也不会发出pre_save或post_save信号(这是调用save()的结果),也不会执行auto_now字段option@NoamG我认为这是一种罕见的情况,在这种情况下,
update()
行为正是我们所需要的。在大多数情况下,您可能只需执行
Clazz.\u meta.get\u field\u by\u name(field\u name)[0]
。谢谢@naktini。已更改。(1.10)模型类。_meta.get_field_by_name(field_name)[0]在do_to_模型中似乎对我不起作用-更改为:模型类。_meta.get_field(field_name)TL;DR不要使用
auto\u now\u add
use
default
。此示例的一个警告是
datetime.datetime.now
返回一个原始日期时间。使用timezo