37

シグナルを使用した再帰に関するStackOverflowの投稿は多数post_saveあり、コメントと回答は圧倒的に多くなっています。「save()をオーバーライドしないのはなぜですか」、またはでのみ起動される保存ですcreated == True

使用しないのは良いケースだとsave()思います。たとえば、注文モデルとは完全に別の注文履行データを処理する一時的なアプリケーションを追加しています。

フレームワークの残りの部分は、フルフィルメントアプリケーションを幸いにも認識せず、post_saveフックを使用すると、すべてのフルフィルメント関連のコードがOrderモデルから分離されます。

フルフィルメントサービスを削除しても、コアコードについては何も変更する必要はありません。フルフィルメントアプリを削除しました。それだけです。

それで、post_saveシグナルが同じハンドラーを2回起動しないようにするための適切な方法はありますか?

4

10 に答える 10

91

シグナルハンドラに保存する代わりに更新を使用できます

queryset.filter(pk = instance.pk).update(....)
于 2012-05-31T19:51:21.307 に答える
39

信号を切断しないでください。シグナルが切断されている間に同じタイプの新しいモデルが生成された場合、ハンドラー関数は起動されません。シグナルはDjango全体でグローバルであり、複数のリクエストを同時に実行できるため、失敗するリクエストもあれば、post_saveハンドラーを実行するリクエストもあります。

于 2013-11-12T17:47:05.297 に答える
37

このソリューションについてどう思いますか?

@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()
于 2015-02-06T16:07:09.073 に答える
27

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.
于 2013-10-31T12:28:37.417 に答える
19

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)
于 2012-05-31T19:43:25.280 に答える
4

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'
于 2014-01-15T19:43:47.577 に答える
4

raw引数をチェックインしてから、の代わりにpost_save呼び出すこともできます。save_basesave

于 2014-03-21T13:31:16.137 に答える
0

これをチェックしてください...

ここのドキュメントで読むことができるように、各シグナルには独自の利点がありますが、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/

よろしく!

于 2018-02-21T04:44:58.653 に答える
0

モデルの.objects.update()方法は信号をバイパスしますpost_save

このようなものを試してください:

from django.db import models
from django.db.models.signals import post_save


class MyModel(models.Model):

    name = models.CharField(max_length=200)
    num_saves = models.PositiveSmallIntegerField(default=0)

    @classmethod
    def post_save(cls, sender, instance, created, *args, **kwargs):
        MyModel.objects.filter(id=instance.id).update(save_counter=instance.save_counter + 1)

post_save.connect(MyModel.post_save, sender=MyModel)

.save()この例では、オブジェクトに名前があり、呼び出されるたびに.num_savesプロパティがインクリメントされますが、再帰はありません。

于 2020-10-28T17:00:49.830 に答える
0

再帰を回避するためのdjangoのpost_savesingalで、「作成された場合」チェックが必要です

from django.dispatch import receiver
from django.db.models.signals import post_save

@receiver(post_save, sender=DemoModel)
def _post_save_receiver(sender,instance,created, **kwargs):
    if created:            
       print('hi..')
       instance.save()
于 2021-01-17T16:45:14.070 に答える