Как создать декоратор 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