Как создать декоратор Django, который создает сигнал для модели?

Я хочу создать пользовательский декоратор в Django, который я могу поместить на свою модель, и он создает сигнал с моей бизнес-логикой. Логика одинакова для каждого сигнала, единственное, что меняется - это модель, к которой он привязывается. Я не могу понять, как этого добиться.

При наличии такой модели, как:

class Post(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)

В идеале я хотел бы получить функциональность с моим декоратором, чтобы поместить его на мою модель, например:

@custom_signal_decorator()
class Post(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)

И это создаст сигнал, подобный этому:

@receiver(post_save, sender=sender)
def notify_created(sender, instance, created, **kwargs):
    channel_layer = channels.layers.get_channel_layer()
    group_name = f'notifications_{instance.company.id}'
    async_to_sync(channel_layer.group_send)(
        group_name,
        {
            "type": "notify_change",
        }
    )

Конечной целью является отслеживание изменений в реальном времени путем отправки уведомления на фронтенд о создании нового поста. Все это работает при создании сигнала вручную для каждой модели, но я чувствую, что декоратор был бы более разумным, чтобы поместить его на модели, которые мы хотим отслеживать.

Любой вклад в то, как я могу реализовать это, был бы полезен, или другой способ, который, по вашему мнению, может работать также хорошо без использования декоратора. Спасибо.

Я пытался создать собственный декоратор, но я не могу понять, как динамически отправлять модель внутрь декоратора, чтобы он отслеживал эту модель, в основном я не понимаю, как это работает.

Вы можете сделать декоратор, который использует переданный класс модели для регистрации сигнала:

from django.db.models.signals import post_save


def notify_created(sender, instance, created, **kwargs):
    channel_layer = channels.layers.get_channel_layer()
    group_name = f'notifications_{instance.company.id}'
    async_to_sync(channel_layer.group_send)(
        group_name,
        {
            'type': 'notify_change',
        },
    )


def custom_signal_decorator(model):
    post_save.connect(notify_created, sender=model)
    return model

и используйте его так:

@custom_signal_decorator
class Post(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)

Мы можем проверить, содержит ли модель поле company_id, и таким образом предотвратить добавление декоратора к модели, несовместимой с сигналом:

def custom_signal_decorator(model):
    model._meta.get_field('company_id')
    post_save.connect(notify_created, sender=model)
    return model

Примечание: Вы можете повысить производительность запроса первичного ключа связанного объекта, используя company_id вместо company.id. Это позволит получить прямой доступ к значению, хранящемуся в модели, и таким образом сэкономить на дополнительном обращении к базе данных.

Вернуться на верх