【问题标题】:How to get a models with highest value grouped by related model in django?如何在 django 中获取按相关模型分组的具有最高价值的模型?
【发布时间】:2019-03-08 13:00:23
【问题描述】:

我想在 django 中执行这个查询:

For each sensor, select it's latest message

在 SQL 中是这样的

SELECT * FROM (SELECT * FROM messages order by date_entered DESC) as order GROUP BY sensor_id

模型是这样定义的

class BaseModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    date_entered = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    deleted = models.IntegerField(default=0)

    class Meta:
        abstract = True
        ordering = ['date_modified']

class Device(BaseModel):

    user =              models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)

    name =              models.CharField(max_length=255, blank=False)
    identifier =        models.CharField(max_length=31, blank=False)
    imei =              models.CharField(max_length=31, blank=False)
    status =            models.CharField(max_length=31, blank=False)
    settings =          models.TextField()


class DeviceMessage(BaseModel):


    device =            models.ForeignKey(Device, on_delete=models.SET_NULL, blank=True, null=True)
    user =              models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)

    latitude =          models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
    longitude =         models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
    altitude =          models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    signal_strength =   models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
    battery =           models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)

    satellites =        models.IntegerField(blank=True, null=True)
    size =              models.IntegerField(blank=True, null=True)

    raw =               models.TextField(blank=True, null=True)

在 django 中可以实现吗?

基本上就是这个问题Using ORDER BY and GROUP BY together

【问题讨论】:

  • 在 SQL 中不会是这样的。
  • 基本上就是这个问题stackoverflow.com/questions/10030787/…
  • 当然有可能,实际上非常简单。假设您在两个模型之间有关系(可能是ForeignKey)。我们当然需要看看你的模型是什么样子的。
  • 我添加了模型的定义
  • 谢谢。我注意到您的模型并非都直接从models.Model 扩展而来,而是从您的BaseModel 扩展而来。我自己从未真正这样做过,如果您打算这样做,建议您阅读文档here。 (特别是,如果 - 似乎很可能 - 您的 BaseModel 仅存在以节省您手动将这些字段添加到每个模型的时间,并且不应代表其自己的数据库表,您可能希望将其设为 Abstract 基类。 )

标签: python sql django


【解决方案1】:

从 Django 1.11 开始,您可以使用 Subquery feature 来注释数据:

from django.db.models import OuterRef, Subquery

latest = DeviceMessage.objects.filter(device_id=OuterRef('pk')).order_by('-date_entered')

devices = Device.objects.annotate(latest_message_id=Subquery(latest.values('pk')[:1]))
message_ids = [d.latest_message_id for d in devices]
for message in DeviceMessage.objects.filter(pk__in=message_ids).select_related('device'):
    print(device.name, message)

......

【讨论】:

    【解决方案2】:

    “对于每个传感器,选择它的最新消息”

    我假设“传感器”指的是Device 模型。在那种情况下(因为您现在已经澄清 BaseModel 是一个抽象基类),它就这么简单(sensor 是您要查询的特定 Device 实例):

    sensor.devicemessage_set.order_by("-date_entered")[0]
    

    如果您想同时查找多个传感器的这些信息,最好使用annotation,例如:

    from django.db.models import Max
    ...
    Device.objects.all().annotate(latest_msg=Max("devicemssage_set__date_entered"))
    

    这为每个Device 对象提供了一个名为latest_msg 的(临时)属性,其中包含您想要的信息。

    【讨论】:

    • 我需要它同时用于所有传感器/设备。注释将仅返回 date_entered 值,对吗?我需要处理整个消息对象(纬度和经度是我想要得到的)
    • 好吧,我的 2 个代码 sn-ps 中的第一个引用了与最新消息相对应的整个对象 - 然后您可以从中访问所有字段。如果这仍然不能回答你的问题,那么我想我需要了解更多关于你实际需要的细节。
    • 嗯,我知道第一个 sn-p 会给我我想要的,但仅限于一个传感器。要获取所有消息,我将不得不遍历所有传感器(可能是数十万个),但这似乎并不正确。或者它没有问题?在一个查询中获得结果会非常好,而不是遍历所有传感器。
    • 嗯,这是一个很好的问题 - 不幸的是,我不确定我能否回答。 (并不是说没有答案,只是我自己的 Django 知识还没有达到我可以达到的水平。)我想在最坏的情况下你可以使用raw SQL (尽管这绝对不推荐) .我必须说,尽管我怀疑任何需要在单个页面上呈现“数十万”条记录的 Web 应用程序 - 至少您应该考虑使用分页。
    • 我使用django平台作为rest api。我没有渲染任何东西,只是发送已注册传感器的最新消息:) 当然还有其他过滤器,例如“仅显示上一小时的最新消息”,或过滤坐标。
    猜你喜欢
    • 1970-01-01
    • 2023-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 2013-05-13
    • 1970-01-01
    相关资源
    最近更新 更多