Django モデル ( .save()) で更新/作成が実行されるとき、特定の属性を「ステップイン」して、以前に設定されたものと比較できるようにしたいと考えています (以前に存在していた場合)。
Pre-Save Signalsを考えており、元のモデルを参照して を実行しています.objects.get(instance.id)が、それは無駄に感じます。また、検証はすでに行われていpre_save()ますか?
Django モデル ( .save()) で更新/作成が実行されるとき、特定の属性を「ステップイン」して、以前に設定されたものと比較できるようにしたいと考えています (以前に存在していた場合)。
Pre-Save Signalsを考えており、元のモデルを参照して を実行しています.objects.get(instance.id)が、それは無駄に感じます。また、検証はすでに行われていpre_save()ますか?
モデル検証について:
モデルのsave()メソッドを呼び出すときに、full_clean()が自動的に呼び出されないことに注意してください。
次に、について、pre-save signal保存されているインスタンスがメッセージとともにパラメータとして送信されることに注意してください。以前のバージョンのモデルはデータベースにのみ存在するため、属性の以前の値を他にどこで取得できるかわかりません...
なぜこれをやりたいのかわからないので、言うのは難しいですが、私が今考えている他の解決策:
* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes
詳細を教えていただければ、もっと簡単かもしれません...
編集 :
そうです...カスタムの「foo_has_updated」を発行した場合、変更が保存されているかどうかはわかりません。
この場合、インスタンスの初期化中に関心のある変数をキャッシュして、保存後または保存前のシグナルをキャッチできると思います。
* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.
変数のキャッシュは次のように実行できます。
class CachedModel(models.Model):
cached_vars = [var1, var2, varN]
def __init__(self, *args, **kwargs):
super(CachedModel, self).__init__(*args, **kwargs)
self.var_cache = {}
for var in self.cached_vars:
self.var_cache[var] = copy.copy(getattr(self, var))
またはこのようなもの...次に、シグナルハンドラーで:
def post_save_handler(sender, **kwargs):
instance = kwargs["instance"]
[(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
#[(<initial value>, <saved value>)
そして、あなたはあなたが必要なものを手に入れました(私は思います)!!!
ここに私の考えがあります: properties で遊んでください。
このクラスがあるとします:
class Foo(models.Model):
name = models.CharField()
代わりに、フィールドの名前を変更します (これを初めて実行する場合は、移行は必要ありません)。
class Foo(models.Model):
_name = models.CharField()
@property
def name(self):
return self._name
@name.setter
def name(self, new_value):
if not getattr(self, '_initial_name', False):
self._initial_name = self._name
if new_value != self._initial_name:
self._name_changed = True
else:
self._name_changed = False
self._name = new_value
Foo インスタンスに 2 つの属性を追加しました: '_initial_name' と '_name_changed' およびプロパティ: 'name'。これらはモデル フィールドではなく、データベースに保存されることはありません。また、「name」プロパティがすべてを処理する限り、「_name」フィールドをいじる必要はもうありません。
これで、'pre_save' または 'post_save' シグナル ハンドラーで何が変更されたかを確認できます。
def handle_pre_save(sender, **kwargs):
foo = kwargs['instance']
if getattr(foo, '_name_changed', False):
log.debug("foo changed its name from '%s' to '%s'",
foo._initial_name, foo.name)
保存する前に、データベースに現在保存されている値を尋ねることができます。変更された値のみをログに記録するためにこれを行いましたが、保存するたびに別のデータベース クエリが発生します。
pre_save シグナルを使用して、db レコード (古いバージョン) とインスタンス レコード (更新されたが、db バージョンに保存されていない) を比較できます。
例として、次のようなモデルを取り上げます。
class Person(models.Model):
Name = models.CharField(max_length=200)
pre_save 関数では、インスタンスのバージョンと db のバージョンを比較できます。
def check_person_before_saving(sender, **kwargs):
person_instance = kwargs['instance']
if person_instance.id:
# you are saving a Person that is already on the db
# now you can get the db old version of Person before the updating
# you should wrap this code on a try/except (just in case)
person_db = Person.objects.get(id=person_instance.id)
# do your compares between person_db and person_instance
...
# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)
私はセバスチャン・ピケマルの答えを非常に承認していますが、最終的にはpre_saveとpost_saveシグナルの両方を使用することになりました。をオーバーライドする代わりに__init__()、 で非常によく似た処理pre_saveを行い、 の値をチェック/比較して、post_save特定の条件が満たされた場合にそこからカスタム シグナルを発信します。
それに費やした時間のために、私はまだ彼の答えを受け入れると思います. 私が信号で作業を行っており、彼が初期化中にそれを行っていることを除いて、私たちがどこで大きな違いをしているのかわかりません。