46

Django にはいくつかのモデル継承レベルがあります。

class WorkAttachment(models.Model):
    """ Abstract class that holds all fields that are required in each attachment """
    work            = models.ForeignKey(Work)
    added           = models.DateTimeField(default=datetime.datetime.now)
    views           = models.IntegerField(default=0)

    class Meta:
        abstract = True


class WorkAttachmentFileBased(WorkAttachment):
    """ Another base class, but for file based attachments """
    description     = models.CharField(max_length=500, blank=True)
    size            = models.IntegerField(verbose_name=_('size in bytes'))

    class Meta:
        abstract = True


class WorkAttachmentPicture(WorkAttachmentFileBased):
    """ Picture attached to work """
    image           = models.ImageField(upload_to='works/images', width_field='width', height_field='height')
    width           = models.IntegerField()
    height          = models.IntegerField()

とから継承された多くの異なるモデルがWorkAttachmentFileBasedありWorkAttachmentます。attachment_count添付ファイルが作成されたときに、親作業のフィールドを更新するシグナルを作成したいと考えています。親送信者 ( ) に対して作成されたシグナルがすべての継承されたモデルに対しても実行されると考えるのは論理的ですWorkAttachmentが、そうではありません。これが私のコードです:

@receiver(post_save, sender=WorkAttachment, dispatch_uid="att_post_save")
def update_attachment_count_on_save(sender, instance, **kwargs):
    """ Update file count for work when attachment was saved."""
    instance.work.attachment_count += 1
    instance.work.save()

から継承されたすべてのモデルでこの信号を機能させる方法はありWorkAttachmentますか?

Python 2.7、Django 1.4 プレアルファ

PSネットで見つけた解決策の1つを試しましたが、うまくいきませんでした。

4

9 に答える 9

15

次のようなものを試すことができます:

model_classes = [WorkAttachment, WorkAttachmentFileBased, WorkAttachmentPicture, ...]

def update_attachment_count_on_save(sender, instance, **kwargs):
    instance.work.attachment_count += 1
    instance.work.save()

for model_class in model_classes:
    post_save.connect(update_attachment_count_on_save, 
                      sender=model_class, 
                      dispatch_uid="att_post_save_"+model_class.__name__)

(免責事項:私は上記をテストしていません)

于 2011-10-17T10:45:32.197 に答える
14

私はPythonの(比較的)新しい__init_subclass__メソッドを使用してこれを行いました:

from django.db import models

def perform_on_save(*args, **kw):
    print("Doing something important after saving.")

class ParentClass(models.Model):
    class Meta:
        abstract = True

    @classmethod
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        models.signals.post_save.connect(perform_on_save, sender=cls)

class MySubclass(ParentClass):
    pass  # signal automatically gets connected.

これには、django 2.1 および python 3.6 以降が必要です。@classmethod公式の python docs によると、この行は必須ではありませんが、django モデルと関連するメタクラスを操作する場合は、この行が必要と思われることに注意してください。

于 2019-02-11T19:45:34.050 に答える
8
post_save.connect(my_handler, ParentClass)
# connect all subclasses of base content item too
for subclass in ParentClass.__subclasses__():
    post_save.connect(my_handler, subclass)

ごきげんよう!

于 2014-07-08T06:14:27.333 に答える
6

Michael Herrmann のソリューションは、間違いなくこれを行う最も Django の方法です。はい、ready() 呼び出しでロードされるため、すべてのサブクラスで機能します。

ドキュメントの参照に貢献したいと思います:

実際には、シグナルハンドラーは通常、関連するアプリケーションのシグナルサブモジュールで定義されます。シグナル レシーバーは、アプリケーション構成クラスの ready() メソッドで接続されます。receiver() デコレータを使用している場合は、ready() 内にシグナル サブモジュールをインポートするだけです。

https://docs.djangoproject.com/en/dev/topics/signals/#connecting-receiver-functions

そして警告を追加します:

ready() メソッドはテスト中に複数回実行される可能性があるため、特にテスト内でシグナルを送信する予定がある場合は、シグナルが重複しないように保護する必要があります。

https://docs.djangoproject.com/en/dev/topics/signals/#connecting-receiver-functions

そのため、connect 関数で dispatch_uid パラメータを使用してシグナルの重複を防ぎたい場合があります。

post_save.connect(my_callback, dispatch_uid="my_unique_identifier")

このコンテキストでは、次のことを行います。

for subclass in get_subclasses(WorkAttachment):
    post_save.connect(update_attachment_count_on_save, subclass, dispatch_uid=subclass.__name__)

https://docs.djangoproject.com/en/dev/topics/signals/#preventing-duplicate-signals

于 2015-07-14T09:01:30.393 に答える
2

このソリューションは、一部のモジュールがメモリにインポートされていない場合の問題を解決します。

def inherited_receiver(signal, sender, **kwargs):
    """
    Decorator connect receivers and all receiver's subclasses to signals.

        @inherited_receiver(post_save, sender=MyModel)
        def signal_receiver(sender, **kwargs):
            ...

    """
    parent_cls = sender

    def wrapper(func):
        def childs_receiver(sender, **kw):
            """
            the receiver detect that func will execute for child 
            (and same parent) classes only.
            """
            child_cls = sender
            if issubclass(child_cls, parent_cls):
                func(sender=child_cls, **kw)

        signal.connect(childs_receiver, **kwargs)
        return childs_receiver
    return wrapper
于 2014-04-18T11:35:41.990 に答える
0

コンテンツ タイプを使用してサブクラスを検出することもできます (基本クラスとサブクラスが同じアプリにパッケージ化されている場合)。次のようなものが機能します。

from django.contrib.contenttypes.models import ContentType
content_types = ContentType.objects.filter(app_label="your_app")
for content_type in content_types:
    model = content_type.model_class()
    post_save.connect(update_attachment_count_on_save, sender=model)
于 2013-05-18T22:03:16.893 に答える