【问题标题】:Django ORM: timedelta difference in days between 2 datesDjango ORM:两个日期之间的时间差
【发布时间】:2021-12-24 11:03:11
【问题描述】:

我有一个表示与给定产品相关的费用的数据库表。

这些费用,因为它们是每天的,有一个from_date(开始日期)和to_date(结束日期)。 to_date 可以为空,因为这些费用可能仍在继续。

给定 2 个 Python datetimes、start_dateend_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)

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

    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())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-08
      • 1970-01-01
      • 1970-01-01
      • 2017-05-16
      相关资源
      最近更新 更多