Django ORM:两个日期之间的天数差异
Django ORM: timedelta difference in days between 2 dates
我有一个数据库 table 表示与给定产品相关的费用。
考虑到这些费用是每天的,因此有 from_date
(开始日期)和 to_date
(结束日期)。 to_date
可以为空,因为这些费用可能仍在继续。
给定 2 Python datetime
s、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())
这个表达式总是 returns 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())
但那也是 returns 零:datetime.timedelta(0)
将 start_date
转换为 DateTimeField
解决了问题,下一步将差异转换为 DurationField
。
所以:
Cast(Cast(start_date, output_field=DateTimeField()) - F('to_date'), output_field=DurationField())
这在任何数据库后端都可以正常工作,但为了获得天数差异,您需要将其包装在 ExtractDay 中,如果您使用 SQLite,它将抛出 ValueError: Extract requires native DurationField database support.
。
如果您依赖于 SQLite 并且不能使用 ExtractDay,您可以使用微秒,并通过除以 86400000000 手动将它们转换为天
duration_in_microseconds=ExpressionWrapper(F('to_date') - (Cast(start_date, output_field=DateTimeField())), output_field=IntegerField())
然后
.annotate(duration_in_days=ExpressionWrapper(F('period_duration_microseconds') / 86400000000, output_field=DecimalField())
我有一个数据库 table 表示与给定产品相关的费用。
考虑到这些费用是每天的,因此有 from_date
(开始日期)和 to_date
(结束日期)。 to_date
可以为空,因为这些费用可能仍在继续。
给定 2 Python datetime
s、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())
这个表达式总是 returns 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())
但那也是 returns 零:datetime.timedelta(0)
将 start_date
转换为 DateTimeField
解决了问题,下一步将差异转换为 DurationField
。
所以:
Cast(Cast(start_date, output_field=DateTimeField()) - F('to_date'), output_field=DurationField())
这在任何数据库后端都可以正常工作,但为了获得天数差异,您需要将其包装在 ExtractDay 中,如果您使用 SQLite,它将抛出 ValueError: Extract requires native DurationField database support.
。
如果您依赖于 SQLite 并且不能使用 ExtractDay,您可以使用微秒,并通过除以 86400000000 手动将它们转换为天
duration_in_microseconds=ExpressionWrapper(F('to_date') - (Cast(start_date, output_field=DateTimeField())), output_field=IntegerField())
然后
.annotate(duration_in_days=ExpressionWrapper(F('period_duration_microseconds') / 86400000000, output_field=DecimalField())