Django ORM: разница в днях между двумя датами в виде временной дельты
У меня есть таблица базы данных, в которой представлены расходы, связанные с определенным продуктом.
Эти расходы, учитывая, что они ежедневные, имеют 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.