根据 Django 中的间隔过滤数据

Filter data based on intervals in Django

我有以下型号:

class MyModel(models.Model):
    due = models.DateTimeField(null=True)
    max_days_before_due = models.IntegerField()

我想过滤掉过期的实例或行,即。当 due 减去 max_days_before_due 是过去时。我正在使用这个查询:

current_date = timezone.now()

MyModel.objects.annotate(
    counted_days=Case(
        When(
            due__isnull=False,
            then=ExtractDay(F('due')-current_date)
        ),
        default=0,
        output_field=IntegerField()
    )
).filter(
    max_days_before_due__lt=F('counted_days')
)

我收到此错误:

django_1    |   File "/usr/local/lib/python3.4/site-packages/django/db/backends/utils.py", line 64, in execute
django_1    |     return self.cursor.execute(sql, params)
django_1    | django.db.utils.ProgrammingError: function pg_catalog.timezone(unknown, interval) does not exist
django_1    | LINE 1: ... '2020-08-26T15:03:11.111165+00:00'::timestamptz) AT TIME ZO...
django_1    |                                                              ^
django_1    | HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

当我从注释中删除减法时,错误没有显示,但我需要能够计算时间增量。否则,如果我将 due 设置为 2020-08-30 并且 current_date2020-08-26 它 returns 30 在 counted_days 字段中而不是 4 .

我一直在尝试使用 and these docs 作为参考。我在版本 10 中使用 PostgreSQL,在版本 1.11 中使用 Django,Python 3.4.

编辑

看起来这个任务应该很简单,使用简单的 SQL:

postgres=# SELECT DATE '2020-08-26' + 10;
  ?column?
------------
 2020-09-05

也许有更简单的方法。

经过几个小时的调试后,我设法发现了问题所在以及解决方法,所以我们开始吧。

我保存在数据库中的字段是“带时区的时间戳”。

我设法在 postgres 中重现了 sql 中的错误。选择 TIMESTAMP 并从中减去一个整数时,出现与 Django 日志中显示的相同的错误:

postgres=# SELECT TIMESTAMP '2020-08-26 01:01:00.123456-01:00'  + 10 > DATE '2020-09-01';
ERROR:  operator does not exist: timestamp without time zone + integer
LINE 1: ...ECT TIMESTAMP '2020-08-26 01:01:00.123456-01:00'  + 10 > DAT...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.

正如我在问题更新中所说,从日期对象中添加或减去整数相当容易:

postgres=# SELECT DATE '2020-08-26 01:01:00.123456-01:00'  + 10 > DATE '2020-09-01';
 ?column?
----------
 t
(1 row)

这建议在注释中使用适当的转换:

MyModel.objects.annotate(due_date=Case(
    When(
        due__isnull=False,
        then=Cast('due', output_field=DateField()) - F('max_days_before_due')
    ),
    default=None,
    output_field=DateTimeField()
).filter(due_date__gt=current_date)

和中提琴:

pprint(my_model_query.values('due', 'max_days_before_due', 'due_date')))

Returns:

<MyModelQuerySet [{'due': datetime.datetime(2020, 8, 20, 9, 31, 25, tzinfo=<UTC>), 'max_days_before_due': 30, 'due_date': datetime.date(2020, 7, 21)}]>