Оконные функции Джанго

avatar
Code-Apprentice
1 июля 2021 в 17:28
420
1
1

Вопрос

Как мне рассчитать дельту столбца между двумя последовательными строками в моей базе данных с помощью Django ORM?

Фон

У меня есть следующая модель:

class Foobar(models.Model):
    foo = models.IntegerField()

Я хочу рассчитать дельту между foo для каждой строки в базе данных. Я нашел поддержку Django для функций Windows и функцию Lag(). Имея их в руках, я попробовал что-то вроде этого:

Foobar.objects.annotate(delta=Window(expression=F('foo') - Lag('foo')))

, что дает следующую ошибку:

Traceback (most recent call last):
  File "/usr/lib/python3.8/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "/home/user/.cache/pypoetry/virtualenvs/scraper-2Ye6bxs0-py3.8/lib/python3.8/site-packages/django/db/models/expressions.py", line 1275, in __init__
    raise ValueError(
ValueError: Expression 'CombinedExpression' isn't compatible with OVER clauses.

Для упрощения я просто попытался аннотировать каждую строку значением из предыдущей строки:

fs = Foobar.objects.annotate(prev=Window(expression=Lag('foo'))
for f in fs:
    print(model_to_dict(f))

Однако результирующие словари в моем выражении print() не содержат 'prev'. Что мне не хватает? Как правильно использовать Lag() для вычисления дельты между каждой строкой?

Источник

Ответы (1)

avatar
Abdul Aziz Barkat
1 июля 2021 в 17:42
2

Полученные слова в моем выражении print() не содержат 'prev', хотя.

Это потому, что вы используете model_to_dict, который использует поля, объявленные моделями, для возврата словаря, конечно, он ничего не знает о данных, которые вы аннотировали через свой запрос. Если вы на самом деле проверите модель, значение будет аннотировано:

fs = Foobar.objects.annotate(prev=Window(expression=Lag('foo'))

for f in fs:
    print(f.prev)

Переходя к вычислению дельты, вы, конечно, можете это сделать, просто F('foo') должно быть вне выражения Window:

fs = Foobar.objects.annotate(delta=F('foo') - Window(expression=Lag('foo')))

for f in fs:
    print(f.delta)

Примечание: кажется, вы используете model_to_dict по какой-то причине, возможно, для сериализации данных для использования в API? Это не совсем отличный способ сериализации моделей, вместо этого вам следует изучить использование Django REST framework.

Code-Apprentice
1 июля 2021 в 19:18
0

Спасибо за ответ. Я использовал только model_to_dict(), чтобы увидеть результаты в оболочке Django. Мой фактический производственный код использует сериализаторы DRF или DRF Response() для списков или диктовок.