Я работаю над личным проектом и пытаюсь написать сложный запрос, который:
Получает каждое устройство, принадлежащее определенному пользователю
Получает каждый датчик, принадлежащий каждому из устройств пользователя
Получает последнее записанное значение и временную метку для каждого из датчиков устройств пользователя.
Я использую Sqlite, и мне удалось написать запрос как обычный SQL, однако, по жизни я не могу найти способ сделать это в Django. Я посмотрел на другие вопросы, попытался просмотреть документацию, но безрезультатно.
Мои модели:
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
Я играл с оболочкой django, чтобы найти способ реализовать этот запрос с помощью API QuerySet, но я не могу понять это ...
Самое близкое, что мне удалось получить, это:
>>> sub = SensorData.objects.values('sensor_id', 'value').filter(sensor_id=OuterRef('pk')).order_by('-timestamp')[:1]
>>> Sensor.objects.annotate(data_id=Subquery(sub.values('sensor_id'))).filter(id=F('data_id')).values(...)
Однако у него есть две проблемы:
- Не включает датчики, которые еще не имеют значений в SensorsData
- Если я включаю поле SensorData.values в .values (), я начинаю получать ранее записанные значения датчиков
Если кто-то может показать мне, как это сделать, или хотя бы сказать, что я делаю неправильно, я буду очень признателен!
Спасибо!
P.S. Пожалуйста, извините мои грамматические и орфографические ошибки, я пишу это среди ночи, и я устала.
EDIT:
Исходя из ответов, я должен уточнить:
Я хочу только последнее значение датчика для каждого датчика. Например у меня в сенсордате:
id | sensor_id | value | timestamp|
1 | 1 | 2 | <today> |
2 | 1 | 5 | <yesterday>|
3 | 2 | 3 | <yesterday>|
Только самые последние должны быть возвращены для каждого sensor_id:
id | sensor_id | value | timestamp |
1 | 1 | 2 | <today> |
3 | 2 | 3 | <yesterday>|
Или, если у датчика еще нет данных в этой таблице, я подожду запроса, чтобы вернуть его запись со значением «null» для значения и метки времени (в основном это левое соединение в моем запросе SQL).
EDIT2:
На основании ответа @ivissani мне удалось сделать следующее:
>>> 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)
>>> for x in user_devices.values_list('device__name','sensor_type__name', 'gpio_port','sensordata__value', 'sensor_type__unit__name', 'sensordata__timestamp').order_by('device__name'):
... print(x)
Который, кажется, делает работу.
Это SQL, который он выдает:
SELECT
"devices_device"."name",
"devices_sensortype"."name",
"devices_sensor"."gpio_port",
"devices_sensordata"."value",
"devices_unit"."name",
"devices_sensordata"."timestamp"
FROM
"devices_sensor"
LEFT OUTER JOIN "devices_sensordata" ON (
"devices_sensor"."id" = "devices_sensordata"."sensor_id"
)
INNER JOIN "devices_device" ON (
"devices_sensor"."device_id" = "devices_device"."id"
)
INNER JOIN "devices_sensortype" ON (
"devices_sensor"."sensor_type_id" = "devices_sensortype"."id"
)
INNER JOIN "devices_unit" ON (
"devices_sensortype"."unit_id" = "devices_unit"."id"
)
WHERE
(
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" > ("devices_sensordata"."timestamp")
)
) = True
AND "devices_device"."user_id" = 1
)
ORDER BY
"devices_device"."name" ASC