如何在Django中获得按相关模型分组的具有最高价值的模型?

时间:2019-03-08 13:00:24

标签: python sql django

我想在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

2 个答案:

答案 0 :(得分: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)

......

答案 1 :(得分:0)

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

我假设“传感器”是指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的(临时)属性,该属性可以保存您想要的信息。