私はこのようなモデルを持っています:
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
より良い解決策はありますか?
私は最近、アプリケーションのテスト中にこの状況に直面しました。期限切れのタイムスタンプを「強制」する必要がありました。私の場合、クエリセットの更新を使用してトリックを行いました。このような:
# 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
_は最適な選択ではありません。多くの場合、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
_を使用した場合と同じように動作します。
質問者の提案を使用して、いくつかの関数を作成しました。使用例は次のとおりです。
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)
同様の関数を作成して、それらを再びオンにすることができます。
Auto_now/auto_now_addフィールドを制限して除外するフィールドがわかっている場合は、save()
に_update_fields
_パラメーターを使用することもできます。
https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save
再利用性のためにコンテキストマネージャーの方法を使用しました。
@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()
ブーム。
移行中に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()
私はパーティーに遅れていますが、他のいくつかの答えと同様に、これはデータベースの移行中に使用したソリューションです。他の回答との違いは、これにより、そのようなフィールドが複数ある理由はまったくないという仮定の下で、モデルの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
渡すすべてのモデルクラスのフィールド。
特別なコードなしで_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
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)
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