Django ORM: разница в днях между двумя датами в виде временной дельты

enter image description here

У меня есть таблица базы данных, в которой представлены расходы, связанные с определенным продуктом.

Эти расходы, учитывая, что они ежедневные, имеют from_date (дату, в которую они начались) и to_date (дату, в которую они закончились). to_date может быть нулевым, так как эти расходы могут все еще продолжаться.

Данные 2 Python datetime, start_date и end_date, мне нужно вывести в ORM общее количество потраченных средств за период для my_product.

>>> start_date
datetime.datetime(2021, 8, 20, 0, 0)

>>> end_date
datetime.datetime(2021, 9, 21, 0, 0)

В этом случае ожидаемый результат должен быть следующим:

(-104 * (days between 08/20 and 08/25)) + (-113 * (days between 08/26 and 09/21)

Вот что у меня есть на данный момент:

(
my_product.income_streams
    .values("product")
    .filter(type=IncomeStream.Types.DAILY_EXPENSE)
    .filter(add_to_commission_basis=True)
    .annotate(period_expenses=Case(
        When(Q(from_date__lte=start_date) & Q(to_date__lte=end_date),
 then=ExpressionWrapper( start_date - F('to_date'), output_field=IntegerField()))
    ), # Other When cases...
)
) # Sum all period_expenses results and you've got the solution

Вот что вызывает у меня проблемы:

then=ExpressionWrapper( start_date - F('to_date'), output_field=IntegerField())

Это выражение всегда возвращает 0 (пожалуйста, обратите внимание, поэтому я даже не пытаюсь умножить на value: это был бы следующий шаг).

Очевидно, start_date - F('to_date') не то же самое, что "дайте мне разницу в днях между этими двумя датами".

Вы можете сделать это в Python с помощью timedelta. Каков эквивалент в ORM?

Я пробовал с ExtractDay:

then=ExpressionWrapper( ExtractDay(start_date - F('to_date'))

Но я получаю: django.db.utils.OperationalError: user-defined function raised exception

А также попробовал с DurationField:

then=ExpressionWrapper(start_date - F('to_date'), output_field=DurationField())

Но это также возвращает ноль: datetime.timedelta(0)

Приведение start_date к DateTimeField решает проблему, а приведение разницы к DurationField является следующим шагом.

Со:

Cast(Cast(start_date, output_field=DateTimeField()) - F('to_date'), output_field=DurationField())

Это будет прекрасно работать на любом бэкенде базы данных, но для того, чтобы получить разницу в днях, вам нужно обернуть это в ExtractDay, что приведет к ошибке ValueError: Extract requires native DurationField database support., если вы используете SQLite.

Вернуться на верх