How to use Django model inheritance with signals? How to use Django model inheritance with signals? python python

How to use Django model inheritance with signals?


You could register the connection handler without sender specified. And filter the needed models inside it.

from django.db.models.signals import post_savefrom django.dispatch import receiver@receiver(post_save)def my_handler(sender, **kwargs):    # Returns false if 'sender' is NOT a subclass of AbstractModel    if not issubclass(sender, AbstractModel):       return    ...

Ref: https://groups.google.com/d/msg/django-users/E_u9pHIkiI0/YgzA1p8XaSMJ


The simplest solution is to not restrict on the sender, but to check in the signal handler whether the respective instance is a subclass:

@receiver(post_save)def update_attachment_count_on_save(sender, instance, **kwargs):    if isinstance(instance, WorkAttachment):        ...

However, this may incur a significant performance overhead as every time any model is saved, the above function is called.

I think I've found the most Django-way of doing this: Recent versions of Django suggest to connect signal handlers in a file called signals.py. Here's the necessary wiring code:

your_app/__init__.py:

default_app_config = 'your_app.apps.YourAppConfig'

your_app/apps.py:

import django.appsclass YourAppConfig(django.apps.AppConfig):    name = 'your_app'    def ready(self):        import your_app.signals

your_app/signals.py:

def get_subclasses(cls):    result = [cls]    classes_to_inspect = [cls]    while classes_to_inspect:        class_to_inspect = classes_to_inspect.pop()        for subclass in class_to_inspect.__subclasses__():            if subclass not in result:                result.append(subclass)                classes_to_inspect.append(subclass)    return resultdef update_attachment_count_on_save(sender, instance, **kwargs):    instance.work.attachment_count += 1    instance.work.save()for subclass in get_subclasses(WorkAttachment):    post_save.connect(update_attachment_count_on_save, subclass)

I think this works for all subclasses, because they will all be loaded by the time YourAppConfig.ready is called (and thus signals is imported).


You could try something like:

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__)

(Disclaimer: I have not tested the above)