Как получить скользящую среднюю (или максимальную величину) за период времени с помощью 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.

  1. Я полагаю, что при использовании 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()
)
  1. использование 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
    })
Вернуться на верх