Как получить скользящую среднюю (или максимальную величину) за период времени с помощью Django ORM
Я пытаюсь понять, возможно ли / практично ли использовать ORM Django для получения максимального значения в произвольном временном интервале из базы данных.
Представьте, что ресторан каждый день заказывает ингредиенты, у нас может быть простая модель, которая выглядит следующим образом:
class Order(models.Model):
date = models.DateField()
ingredient = models.CharField()
quantity = models.IntegerField()
Тогда я смогу получать суммарные объемы заказов каждую неделю:
Order.objects.filter(date__gte=start_date, date__lt=end_date)
.annotate(date=TruncWeek("date"))
.values("ingredient", "date")
.annotate(total=Sum("quantity"))
.order_by("ingredient")
Но теперь я хочу определить максимальное количество каждого ингредиента, которое было заказано за любые последовательные 7 (или X) дней в отфильтрованном диапазоне дат.
Возможно ли это сделать в ORM?
Вы можете найти максимальное количество каждого ингредиента, заказанного в течение любого последовательного 7-дневного периода, используя несколько подходов: либо с помощью оконных функций (Postgres db), либо с помощью комбинации анализа списка и запросов ORM.
- Я полагаю, что при использовании Window func это будет примерно так
from django.db.models import Max, Window
from django.db.models.functions import ExtractDay
from django.db.models.expressions import ValueRange
peak_orders = (
Order.objects
.filter(date__gte=start_date, date__lt=end_date)
.annotate(
peak_7day=Window(
expression=Max('quantity'),
partition_by=['ingredient'],
order_by=ExtractDay('date').asc(),
frame=ValueRange(start=-6, end=0)
)
)
.values('ingredient', 'peak_7day')
.distinct()
)
- использование ORM с пониманием списка
from datetime import timedelta
from django.db.models import Sum
results = []
unique_dates = Order.objects.dates('date', 'day')
ingredients = Order.objects.values_list('ingredient', flat=True).distinct()
for ing in ingredients:
current_max = 0
for date in unique_dates:
window_sum = (
Order.objects
.filter(
ingredient=ing,
date__gte=date,
date__lt=date + timedelta(days=7)
)
.aggregate(total=Sum('quantity'))['total'] or 0
)
current_max = max(current_max, window_sum)
results.append({
'ingredient': ing,
'peak_7day': current_max
})