代码之家  ›  专栏  ›  技术社区  ›  72mins

如何创建一个为模型创建信号的Django装饰器?

  •  1
  • 72mins  · 技术社区  · 1 年前

    我想在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",
            }
        )
    

    最终目标是通过在创建新帖子时向前端发送通知来实时跟踪更改。当手工为每个模型创建信号时,这一切都是有效的,但我觉得装饰师会更有意义,把我们想要跟踪的模型放上去。

    任何关于我将如何实现这一点的输入都将是有帮助的,或者您认为在不使用装饰器的情况下也可以使用其他方式。非常感谢。

    我试着自己创建一个自定义装饰器,但我不知道如何在装饰器中动态发送模型,以便它跟踪该模型,这主要是我不了解它是如何工作的。

    1 回复  |  直到 1 年前
        1
  •  1
  •   willeM_ Van Onsem    1 年前

    您可以制作一个装饰器,使用传递的模型类来注册信号:

    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 。这将直接访问存储在模型中的值,从而节省到数据库的额外往返行程。