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_save
from 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.apps

class 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 result

def 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).