Предположим, у меня есть следующая модель:
class TestModel(models.Model):
some_integer = models.IntegerField()
и у меня есть 3 экземпляра этой модели:
TestModel.objects.create(some_integer=100)
TestModel.objects.create(some_integer=50)
TestModel.objects.create(some_integer=20)
и я хотел бы как-то аннотировать набор запросов, чтобы получить следующие результаты:
for obj in TestModel.objects.annotate(difference=...):
print(obj.difference)
=> 50 # 100 - 50
=> 30 # 50 - 30
=> None # we don't have anything created after this record
Есть ли шанс сделать это с помощью наборов запросов Django (2.0) или мне следует сделать это «вручную» в Python? Мы можем с уверенностью предположить, что объекты упорядочены по pk или некоторой дате, поэтому порядок сохраняется.
@WillemVanOnsem мы можем предположить, что QuerySet упорядочен по pk или какой-то дате, я обновил описание этой информацией.
Основываясь на этом Отвечать, если вы хотите сделать это с помощью необработанного SQL, вы можете сделать это следующим образом:
SELECT *,
LEAD("some_integer") OVER(ORDER BY "id") AS "next_val",
"some_integer" - "next_val" AS "difference"
FROM "myapp_testmodel";
Однако из версии Django 2.0
вы можете использовать Оконные функции для создания вышеуказанного запроса:
from django.db.models import Window, F
from django.db.models.functions import Lead
q = TestModel.objects.annotate(
next_val=Window(
expression=Lead('some_integer', offset=1, default=0),
order_by=F('id').asc()
),
difference=F('some_integer')-F('next_val')
)
Примечание: Я использовал 0 в качестве значения по умолчанию (которое будет применено к последнему элементу), чтобы упростить ответ и предотвратить FieldError
, вызванный вычитанием значения Null из Integer. вы можете обработать возврат None для последнего элемента, используя запрос Django Случай.
Спасибо! Я никогда не тратил время на изучение оконных функций, но думаю, что это произойдет сейчас :)
Если я просто аннотирую next_val
, он работает нормально, но если я использую next_val
в другой аннотации, например difference
, он выдает ошибку: window functions are not allowed in GROUP BY
. Что мне здесь не хватает?
Я думаю, что проблема концептуальный заключается в том, что стандартная реляционная база данных не имеет понятия «предыдущий» или «следующий». Элементы могут быть возвращены в порядке любой, если элементы не упорядочены явно.