web-dev-qa-db-ja.com

auto_now / auto_now_addを一時的に無効にします

私はこのようなモデルを持っています:

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

一部のモデルインスタンス(データの移行時に使用)の2つの日付フィールドを上書きしたい。現在のソリューションは次のようになります。

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

より良い解決策はありますか?

86
jedie

私は最近、アプリケーションのテスト中にこの状況に直面しました。期限切れのタイムスタンプを「強制」する必要がありました。私の場合、クエリセットの更新を使用してトリックを行いました。このような:

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

Auto_now/auto_now_addを実際に無効にすることはできません。これらの値を変更する柔軟性が必要な場合、_auto_now_/_auto_now_add_は最適な選択ではありません。多くの場合、defaultを使用したり、save()メソッドをオーバーライドしたりして、オブジェクトを保存する直前に操作を行う方が柔軟です。

defaultとオーバーライドされたsave()メソッドを使用して、問題を解決する1つの方法は、次のようにモデルを定義することです。

_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_を使用した場合と同じように動作します。

52
andreaspelme

質問者の提案を使用して、いくつかの関数を作成しました。使用例は次のとおりです。

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)

同様の関数を作成して、それらを再びオンにすることができます。

24
Frank Henard

Auto_now/auto_now_addフィールドを制限して除外するフィールドがわかっている場合は、save()に_update_fields_パラメーターを使用することもできます。

https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save

16
Apollo Data

再利用性のためにコンテキストマネージャーの方法を使用しました。

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

ブーム。

16
soulseekah

移行中に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()
2
user1172023

私はパーティーに遅れていますが、他のいくつかの答えと同様に、これはデータベースの移行中に使用したソリューションです。他の回答との違いは、これにより、そのようなフィールドが複数ある理由はまったくないという仮定の下で、モデルのall 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_add渡すすべてのモデルクラスのフィールド。

1
mlissner

特別なコードなしで_auto_now_add_をオーバーライドできます。

特定の日付のオブジェクトを作成しようとしたときに、この質問に出会いました。

_Post.objects.create(publication_date=date, ...)
_

ここで、publication_date = models.DateField(auto_now_add=True)

だからこれは私がやったことです:

_post = Post.objects.create(...)
post.publication_date = date
post.save()
_

これは_auto_now_add_を正常にオーバーライドしました。

より長期的な解決策として、saveメソッドをオーバーライドする方法があります。 https://code.djangoproject.com/ticket/1658

1
Pavel Vergeev

update_or_createで動作するソリューションが必要でした。@ andreaspelmeコードに基づいてこのソリューションに到達しました。

変更点は、kwarg skip_modified_updateをsave()メソッドに実際に渡すだけでなく、変更フィールドをskipに設定することでスキップを設定できることだけです。

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

Django-Models.DateTimeField-auto_now_add値の動的な変更 のコピー

さて、私は今日の午後の調査に費やし、最初の問題はモデルオブジェクトをフェッチする方法とコード内の場所です。 serializer.pyのrestframeworkにいます。たとえば、__init__シリアライザーのモデルをまだ持つことができませんでした。 to_internal_valueでは、Fieldを取得した後、この例のようにフィールドプロパティを変更した後、モデルクラスを取得できます。

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
0
Sérgio