17

我有以下模型:

class Assignment(models.Model):
  extra_days = models.IntegerField(default=0)
  due_date = models.DateTimeField()

哪里due_date是作业的截止日期,extra_days是在截止日期后完成作业的额外天数。

我想创建一个查询,返回due_date + extra_days大于当前日期的所有行。这就是我正在做的事情:

from django.utils import timezone
from django.db.models import F
from datetime import datetime

cur_date = timezone.make_aware(datetime.now(), timezone.get_default_timezone())
a = Assignment.objects.filter(extra_days__gt=cur_date - F('due_date'))

当我打印a时,我收到以下错误:

  File "c:\Python27\lib\site-packages\MySQLdb\cursors.py", line 204, in execute
    if not self._defer_warnings: self._warning_check()
  File "c:\Python27\lib\site-packages\MySQLdb\cursors.py", line 117, in _warning
_check
    warn(w[-1], self.Warning, 3)
Warning: Truncated incorrect DOUBLE value: '2013-09-01 02:54:31'

如果我做了一个导致 3.1 天的时差,我假设天差仍然是 3。我认为做这样的事情会更正确:

a = Assignment.objects.filter(due_date__gt=cur_date - timedelta(days=F('extra_days')))

但这也会导致错误。

在不编写原始 SQL 查询的情况下如何做到这一点?

4

4 回答 4

4

似乎我想做的事情是不可能的。我最终写了一个原始查询:

cursor.execute("SELECT * FROM app_assignment WHERE DATE_ADD(due_date, INTERVAL extra_days DAYS) > utc_timestamp()")

我很反感不能使用 ORM 来做一些看似简单的事情,以至于我考虑尝试 SQLAlchemy,但原始查询工作正常。我总是尝试变通方法以确保我可以使用 ORM,但我将使用原始 SQL 来处理复杂的查询。

于 2013-09-03T02:58:50.553 回答
4

这取决于您使用的数据库后端,它似乎是 PostgreSQL。

PostgreSQL 可以直接减去日期,因此以下将起作用:

from django.db.models import F, Func
from django.db.models.functions import Now

class DaysInterval(Func):
    function = 'make_interval'
    template = '%(function)s(days:=%(expressions)s)'

qs = Assignment.objects.annotate(remaining_days=F('due_date') - Now())
qs.filter(remaining_days__lt=DaysInterval(F('extra_days')))

这将产生以下 SQL:

SELECT "assignments_assignment"."id", 
       "assignments_assignment"."extra_days", 
       "assignments_assignment"."due_date", 
       ("assignments_assignment"."due_date" - STATEMENT_TIMESTAMP()) AS "remaining_days" 
FROM   "assignments_assignment" 
WHERE  ("assignments_assignment"."due_date" - STATEMENT_TIMESTAMP())
        < (make_interval(DAYS:="assignments_assignment"."extra_days"))

有关其他数据库后端中的日期差异计算,请参阅DatediffMichael Brooks 创建的函数。

于 2018-08-30T18:26:19.110 回答
3

据我所知,您不能将 F() 对象作为参数传递给另一个函数,因为 F() 基类是 tree.Node 类型,用于存储树形图的类,主要用于 ORM 中的过滤器构造.

见 F() 定义在django/db/models/expression.py和节点在django/utils/tree.py(django 1.3.4)

class ExpressionNode(tree.Node):
    ...

class F(ExpressionNode):
    """
    An expression representing the value of the given field.
    """
    def __init__(self, name):
        super(F, self).__init__(None, None, False)
        self.name = name

    def __deepcopy__(self, memodict):
        obj = super(F, self).__deepcopy__(memodict)
        obj.name = self.name
        return obj

    def prepare(self, evaluator, query, allow_joins):
        return evaluator.prepare_leaf(self, query, allow_joins)

    def evaluate(self, evaluator, qn, connection):
        return evaluator.evaluate_leaf(self, qn, connection)

你可以做类似的事情

Assignment.objects.filter(due_date__gt=F('due_date') - timedelta(days=1))

但不是

Assignment.objects.filter(due_date__gt=cur_date - timedelta(days=F('extra_days')))

如果我错了,请纠正我。希望这个小小的帮助。

于 2013-09-01T18:25:32.957 回答
2

以防万一其他人寻找这个,这里有一些可能值得研究的东西。

我正在使用 Django 1.4,并且遇到了与 OP 完全相同的问题。似乎该问题可能是由于timedelta并且datetime需要在发送到数据库之前进行评估,但该F对象本质上只能在数据库中解决。

我注意到在 Django 1.8 中,引入了一个新的DurationField,看起来它可以像 python 的 timedelta 一样直接工作。这应该意味着不需要在 IntegerField 上查找 F 对象的 timedelta,理论上可以使用 DurationField,然后 F 对象根本不需要在 timedelta 中。不幸的是,由于依赖关系,我目前无法将我的项目升级到 1.8 并测试这个理论。

如果其他人遇到这个问题并且能够测试我的建议,我很想知道。如果我解决了我的依赖关系并且可以升级到 1.8,那么我一定会回发我的结果。

于 2016-02-02T03:12:06.013 回答