Разница с предыдущим объектом в аннотации набора запросов django

Предположим, у меня есть следующая модель:

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 или некоторой дате, поэтому порядок сохраняется.

Я думаю, что проблема концептуальный заключается в том, что стандартная реляционная база данных не имеет понятия «предыдущий» или «следующий». Элементы могут быть возвращены в порядке любой, если элементы не упорядочены явно.

Willem Van Onsem 13.09.2018 16:38

@WillemVanOnsem мы можем предположить, что QuerySet упорядочен по pk или какой-то дате, я обновил описание этой информацией.

mrbox 13.09.2018 16:40
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
2
1 018
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основываясь на этом Отвечать, если вы хотите сделать это с помощью необработанного 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 Случай.

Спасибо! Я никогда не тратил время на изучение оконных функций, но думаю, что это произойдет сейчас :)

mrbox 14.09.2018 19:05

Если я просто аннотирую next_val, он работает нормально, но если я использую next_val в другой аннотации, например difference, он выдает ошибку: window functions are not allowed in GROUP BY. Что мне здесь не хватает?

TheKalpit 19.06.2020 20:40

Другие вопросы по теме