5

我有一个具有Booking日期时间字段的模型。我想知道预订涵盖多少天。我可以在 Python 中做到这一点,但我需要这个值来做进一步的注释。startend

这是我尝试过的:

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足以让它走出门外?

4

3 回答 3

13

我编写了几个数据库函数来转换和截断日期,以解决 PostgreSQL 下的这两个问题。我使用的内部函数是特定于 DB 的 ☹</ p DATE_PART>DATE_TRUNC

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
于 2016-07-20T09:42:41.220 回答
11

这个问题还有另一个简单的解决方案。您可以使用:

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
于 2017-11-10T11:44:13.847 回答
0

如果您使用的是MYSQL数据库,您可以使用自定义 DB 函数来执行此操作,

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__)
于 2018-08-10T04:06:23.280 回答