Django QuerySets 复杂查询

在 django 中使用 querysets API 进行复杂查询,要求同时获得以下信息:

  • 获取某个用户的设备
  • 获取每个设备所属的传感器
  • 获取每个传感器最近记录的值和时间戳

使用 plain SQL 可以轻松实现此查询,但在 django 中却遇到了困难。尝试了 django shell 和查阅文档,但仍无法找到解决方案。

最终使用的模型如下:

class User(AbstractBaseUser):
    email = models.EmailField()

class Device(models.Model):
    user = models.ForeignKey(User)
    name = models.CharField()

class Unit(models.Model):
    name = models.CharField()

class SensorType(models.Model):
    name = models.CharField()
    unit = models.ForeignKey(Unit)

class Sensor(models.Model):
    gpio_port = models.IntegerField()
    device = models.ForeignKey(Device)
    sensor_type = models.ForeignKey(SensorType)

class SensorData(models.Model):
    sensor = models.ForeignKey(Sensor)
    value = models.FloatField()
    timestamp = models.DateTimeField()

SQL 查询如下:

SELECT acc.email, 
       dev.name as device_name, 
       stype.name as sensor_type,
       sen.gpio_port as sensor_port,
       sdata.value as sensor_latest_value, 
       unit.name as sensor_units, 
       sdata.latest as value_received_on
FROM devices_device as dev
INNER JOIN accounts_user  as acc on dev.user_id = acc.id
INNER JOIN devices_sensor  as sen on sen.device_id = dev.id
INNER JOIN devices_sensortype as stype on stype.id = sen.sensor_type_id
INNER JOIN devices_unit as unit on unit.id = stype.unit_id
LEFT JOIN (
            SELECT MAX(sd.timestamp) latest, sd.value, sensor_id
            FROM devices_sensordata as sd
            INNER JOIN devices_sensor as s ON s.id = sd.sensor_id
        GROUP BY sd.sensor_id) as sdata on sdata.sensor_id= sen.id
WHERE acc.id = 1
ORDER BY dev.id
  1. 解决方案

在 django 中,使用 SubqueryExists 来实现此查询:

latest_sensor_data = Sensor.objects.annotate(is_latest=~Exists(SensorData.objects.filter(sensor=OuterRef('id'),timestamp__gt=OuterRef('sensordata__timestamp')))).filter(is_latest=True)
user_devices = latest_sensor_data.filter(device__user=1)

生成的 SQL 如下:

    SELECT
  "devices_sensor"."id",
  "devices_sensor"."gpio_port",
  "devices_sensor"."sensor_type_id",
  "devices_sensor"."device_id",
  (
    NOT EXISTS(
      SELECT
        U0."id",
        U0."sensor_id",
        U0."value",
        U0."timestamp"
      FROM
        "devices_sensordata" U0
      WHERE
        (
          U0."sensor_id" = ("devices_sensor"."id")
          AND U0."timestamp" > (
            SELECT
              "devices_sensordata"."timestamp"
            FROM
              "devices_sensordata"
            WHERE
              "devices_sensordata"."sensor_id" = ("devices_sensor"."id")
            ORDER BY
              "devices_sensordata"."timestamp" DESC
            LIMIT 1
          )
        )
    ) = True
  ) AS "is_latest"
FROM
  "devices_sensor"
ORDER BY
  "devices_sensor"."id" ASC

最终可以通过 user_devices.values_list('device__name','sensor_type__name', 'gpio_port','sensordata__value', 'sensor_type__unit__name', 'sensordata__timestamp').order_by('device__name') 获取查询结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值