Пользовательский TruncFunc в Django ORM

У меня есть модель Django со следующей структурой:

class BBPerformance(models.Model):
    marketcap_change = models.FloatField(verbose_name="marketcap change", null=True, blank=True)
    bb_change = models.FloatField(verbose_name="bestbuy change", null=True, blank=True)
    created_at = models.DateTimeField(verbose_name="created at", auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name="updated at", auto_now=True)

Я хотел бы иметь Avg агрегатную функцию для объектов за каждые 3 дня.
например, я пишу набор запросов, который делает эту агрегацию для каждого дня или с чем-нибудь вроде TruncDay функции.

queryset = BBPerformance.objects.annotate(day=TruncDay('created_at')).values('day').annotate(marketcap_avg=Avg('marketcap_change'),bb_avg=Avg('bb_change')

Как я могу получить набор запросов, состоящий из агрегированного значения с интервалом в 3 дня и индекса второго дня этого интервала?

Полагаю, что это невозможно на уровне БД (а Trunc - это функция уровня БД), поскольку в Postgres и Oracle поддерживаются только месяц, дни недели и т.д.

Так что я бы предложил использовать TruncDay и затем добавить код python для группировки по 3 дням.

<

Если вы получите разницу в днях между датой каждого ряда и минимальной датой, вы можете взять Mod этой разницы, чтобы определить, сколько дней нужно сдвинуть, чтобы получить "среднюю" дату. Затем эту среднюю дату можно сгруппировать с помощью запроса значений

import datetime
from django.db.models import F, IntegerField, Avg, Min, DurationField, DateField
from django.db.models.functions import Cast, Mod, Extract

BBPerformance.objects.order_by(
    'created_at'
).annotate(
    diff=F('created_at__date') - BBPerformance.objects.aggregate(min=Min('created_at__date'))['min']
).annotate(
    diff_days=Cast(Extract('diff', 'days'), output_field=IntegerField())
).annotate(
    to_shift=Mod('diff_days', 3) - 1
).annotate(
    grouped_date=Cast(F('created_at__date') - Cast(F('to_shift') * datetime.timedelta(days=1), output_field=DurationField()), output_field=DateField())
).order_by(
    'grouped_date'
).values(
    'grouped_date'
).annotate(
    marketcap_avg=Avg('marketcap_change'),
    bb_avg=Avg('bb_change')
)
Вернуться на верх