如何以天为单位注释日期时间的差异

时间:2016-07-20 09:24:22

标签: django datetime django-annotate django-database-functions

我的Booking模型包含startend日期时间字段。我想知道预订的天数。我可以在Python中执行此操作,但我需要此值以进行进一步注释。

这是我尝试过的:

In [1]: Booking.objects.annotate(days=F('end')-F('start'))[0].days
Out[1]: datetime.timedelta(16, 50400)

这里有一些问题:

  • 我想要一个整数(或我可以在计算中使用的其他数字类型)作为输出,而不是timedelta。设置output_field在这里没有任何意义。
  • 我的总和基于日期。像这样的减法,没有删除时间可能会导致整天停止。

在Python中我会做(end.date() - start.date()).days + 1。我怎样才能在数据库中进行,最好是通过ORM(例如数据库函数),但RawSQL就足以让它出来了?

3 个答案:

答案 0 :(得分:7)

还有另一个简单的解决方法。您可以使用:

from django.db.models import F
from django.db.models.functions import ExtractDay

然后:

Booking.objects.annotate(days=(ExtractDay(F('end')-F('start'))+1))[0].days

答案 1 :(得分:5)

我编写了几个数据库函数来转换和截断日期以解决PostgreSQL下的两个问题。我正在使用的DATE_PARTDATE_TRUNC内部函数是特定于DB的☹

from django.db.models import Func

class DiffDays(Func):
    function = 'DATE_PART'
    template = "%(function)s('day', %(expressions)s)"

class CastDate(Func):
    function = 'date_trunc'
    template = "%(function)s('day', %(expressions)s)"

然后我可以:

In [25]: Booking.objects.annotate(days=DiffDays(CastDate(F('end'))-CastDate(F('start'))) + 1)[0].days
Out[25]: 18.0

答案 2 :(得分:1)

如果使用的是 MYSQL 数据库,则可以使用 自定义数据库功能 作为

from django.db.models.functions import Func


class TimeStampDiff(Func):
    class PrettyStringFormatting(dict):
        def __missing__(self, key):
            return '%(' + key + ')s'

    def __init__(self, *expressions, **extra):
        unit = extra.pop('unit', 'day')
        self.template = self.template % self.PrettyStringFormatting({"unit": unit})
        super().__init__(*expressions, **extra)

    function = 'TIMESTAMPDIFF'
    template = "%(function)s(%(unit)s, %(expressions)s)"



用法

from django.db.models import F, IntegerField

booking_queryset = Booking.objects.annotate(
    days=TimeStampDiff(F('start'), F('end'), output_field=IntegerField()))
if booking_queryset.exist():
    print(booking_queryset[0].__dict__)