【问题标题】:Django signals - how do I send what was saved in the model using post_save?Django 信号 - 如何使用 post_save 发送模型中保存的内容?
【发布时间】:2020-09-28 17:19:12
【问题描述】:

尝试使用信号通过 websockets 发送使用 .save() 保存的最后一条记录。我在data 中输入了什么?

#models.py
from django.db import models
from django.db.models.signals import post_save
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

class DataModel(models.Model):
    time = models.DateTimeField()
    value = models.FloatField()

    def __str__(self):
        return str(self.time)

def save_post(sender, instance, **kwargs):
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        "echo_group",
        {"type": "on.message", "data": data},
    )

post_save.connect(save_post, sender=DataModel)

我想我可以只获取索引最高的记录并发送它,但想知道是否有更优雅的解决方案。

【问题讨论】:

    标签: python django django-models django-channels django-signals


    【解决方案1】:

    前奏:处理通道水化

    要知道上次修改的内容,您需要一个上次修改的字段。这是一个众所周知的模式,Django 使用 DateTimeFields 的 auto_now=True 参数来帮助解决它。这些字段不可编辑,并尽可能使用数据库触发器来获得结果(这就是它们不可编辑的原因)。

    如上所述,这很常见,所以我通常使用基本模型:

    class AuditableBase(models.Model):
        """
        Base class that adds created_at and last_modified fields for audit purposes.
        """
    
        created_at = models.DateTimeField(auto_now_add=True)
        last_modified = models.DateTimeField(auto_now=True)
    
        class Meta:
            abstract = True
    

    现在,无论是否保存,获取最后修改的记录并用最后修改的通道水合一个通道是微不足道的:

    class DataModel(AuditableBase):
        time = models.DateTimeField()
        value = models.FloatField()
    
        def __str__(self):
            return str(self.time)
    
    # On channel start:
    latest = DataModel.objects.latest('last_modified')
    

    信号处理程序

    但是,如果我们使用 post save 信号,我们已经在“instance”参数中拥有刚刚保存的对象。为了方便的转换成json,我们可以使用model_to_dict和DjangoJSONEncoder来处理大部分问题:

    from django.forms.models import model_to_dict
    from django.core.serializers import DjangoJSONEncoder
    import json
    
    def save_post(sender, instance, **kwargs):
        channel_layer = get_channel_layer()
        data = model_to_dict(instance)
        json_data = json.dumps(data, cls=DjangoJSONEncoder)
        async_to_sync(channel_layer.group_send)(
            "echo_group",
            {"type": "on.message", "data": json_data},
        )
    

    Model_to_dict 将模型转换为字典,并且可以使用fields=(显式包含)或exclude=(显式排除)进行限制。 DjangoJSONEncoder 处理时态数据,json 的默认编码器不支持。

    【讨论】:

      【解决方案2】:

      我认为instance.__dict__ 是您想要的。它将所有模型属性转换为字典键值对

      所以你可以做类似的事情

      def save_post(sender, instance, **kwargs):
          channel_layer = get_channel_layer()
          async_to_sync(channel_layer.group_send)(
              "echo_group",
              {"type": "on.message", "data": instance.__dict__},
          )
      

      更新

      我没有提到instance 是刚刚保存并触发post_save 信号的实际对象。所以在给定的问题中instance 将类似于instance = DataModel.objects.create(**kwargs)

      【讨论】:

      • 不确定我是否关注。 instance.__dict__ 会返回插入模型的最后一条记录吗?
      • 不,它返回实例触发器,即刚刚保存并触发post_save 信号的对象。我也更新了我的答案
      猜你喜欢
      • 1970-01-01
      • 2011-01-10
      • 2012-05-31
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 2012-06-04
      相关资源
      最近更新 更多