_post_save
_シグナルを使用した再帰に関するスタックオーバーフローの投稿が多数あります。これらには、コメントと回答が圧倒的に多くなっています。「なぜsave()をオーバーライドしないか」または_created == True
_でのみ発生する保存。
さて、save()
を使用しないことには良いケースがあると思います-たとえば、注文処理データを注文モデルとは完全に分離して処理する一時アプリケーションを追加しています。
フレームワークの残りの部分はフルフィルメントアプリケーションに気付かず、post_saveフックを使用すると、フルフィルメント関連のすべてのコードがOrderモデルから分離されます。
フルフィルメントサービスを削除しても、コアコードは変更する必要がありません。フルフィルメントアプリを削除しました。これで完了です。
では、post_save信号が同じハンドラーを2回起動しないようにするための適切なメソッドはありますか?
このソリューションについてどう思いますか?
@receiver(post_save, sender=Article)
def generate_thumbnails(sender, instance=None, created=False, **kwargs):
if not instance:
return
if hasattr(instance, '_dirty'):
return
do_something()
try:
instance._dirty = True
instance.save()
finally:
del instance._dirty
デコレータを作成することもできます
def prevent_recursion(func):
@wraps(func)
def no_recursion(sender, instance=None, **kwargs):
if not instance:
return
if hasattr(instance, '_dirty'):
return
func(sender, instance=instance, **kwargs)
try:
instance._dirty = True
instance.save()
finally:
del instance._dirty
return no_recursion
@receiver(post_save, sender=Article)
@prevent_recursion
def generate_thumbnails(sender, instance=None, created=False, **kwargs):
do_something()
シグナルハンドラで保存する代わりに更新を使用できます
quersyset.filter(pk = instance.pk).update(....)
信号を切断しないでください。信号が切断されている間に同じタイプの新しいモデルが生成された場合、ハンドラー関数は呼び出されません。シグナルはDjango全体でグローバルであり、いくつかのリクエストは同時に実行されている可能性があり、一部は失敗し、その他はpost_saveハンドラーを実行します。
モデルにsave_without_signals()
メソッドを作成する方がより明示的だと思います。
class MyModel()
def __init__():
# Call super here.
self._disable_signals = False
def save_without_signals(self):
"""
This allows for updating the model from code running inside post_save()
signals without going into an infinite loop:
"""
self._disable_signals = True
self.save()
self._disable_signals = False
def my_model_post_save(sender, instance, *args, **kwargs):
if not instance._disable_signals:
# Execute the code here.
post_save
関数内で信号を切断してから再接続してみてはいかがですか。
def my_post_save_handler(sender, instance, **kwargs):
post_save.disconnect(my_post_save_handler, sender=sender)
instance.do_stuff()
instance.save()
post_save.connect(my_post_save_handler, sender=sender)
post_save.connect(my_post_save_handler, sender=Order)
Model.save()の代わりにqueryset.update()を使用する必要がありますが、他のことに注意する必要があります。
これを使用するとき、新しいオブジェクトを使用したい場合は、彼のオブジェクトを再度取得する必要があることに注意してください。これは、セルフオブジェクトを変更しないためです。次に例を示します。
>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> print el.text
>>> ''
したがって、新しいオブジェクトを使用したい場合は、もう一度実行する必要があります。
>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> el = MyModel.objects.get(pk=1) # Do it again
>>> print el.text
>>> 'Updated'
post_save
のraw
引数を確認し、save
の代わりにsave_base
を呼び出すこともできます。
これをチェックしてください...
ここのドキュメントで読むことができるように、各信号には独自の利点がありますが、私はpre_saveおよびpost_save信号に留意するためにいくつかのことを共有したいと思いました。
どちらも、モデルの.save()が呼び出されるたびに呼び出されます。つまり、モデルインスタンスを保存すると、信号が送信されます。
post_save内のインスタンスでsave()を実行すると、多くの場合、終了しないループが作成されるため、最大再帰深度超過エラーが発生します-.save()を正しく使用しない場合のみ。
pre_saveは、save()を呼び出す必要がないため、インスタンスデータだけを変更するのに最適です。これにより、上記の可能性が排除されます。 save()を呼び出す必要がないのは、pre_saveシグナルが文字通り保存される直前を意味するためです。
シグナルは、他のシグナルを呼び出したり、(Celeryの場合)遅延タスクを実行したりすることができます。
ソース: https://www.codingforentrepreneurs.com/blog/post-save-vs-pre-save-vs-override-save-method/
よろしく!!