Разница во времени Django с объектом F

у меня есть следующие модели:

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

здесь due_date является датой назначения и extra_days - количество дней после даты, чтобы закончить задание.

Я хочу создать запрос, который возвращает все строки, где due_date + extra_days больше текущей даты. Вот что я делаю:--9-->

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:Python27libsite-packagesMySQLdbcursors.py", line 204, in execute
    if not self._defer_warnings: self._warning_check()
  File "c:Python27libsite-packagesMySQLdbcursors.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 ответов


Кажется, что то, что я пытаюсь сделать, невозможно. Я закончил писать необработанный запрос:

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

меня так отталкивало, что я не мог использовать ORM для выполнения чего-то настолько простого, что я решил попробовать SQLAlchemy, но необработанный запрос работает нормально. Я всегда пробовал обходные пути, чтобы убедиться, что я могу использовать ORM, но я буду использовать raw SQL для сложных запросов.


насколько я знаю , вы можете не передайте объект F () как params к другой функции, так как F () базовый класс является деревом.Тип узла, класс для хранения древовидного графа, который в основном используется для построения фильтров в ORM.

см. F () define at 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')))

поправьте меня, если я ошибся. Надеюсь, это немного помощь.


на всякий случай, если кто-то еще ищет это, вот что может быть стоит посмотреть.

Я использую Django 1.4 и сталкиваюсь с той же проблемой, что и OP. Кажется, что проблема, вероятно, из-за timedelta и datetime необходимо оценить перед отправкой в базу данных, но F объект по своей сути будет разрешаться только в базе данных.

Я заметил, что в Django 1.8, новый DurationField было введено, что выглядит как будто это будет работать напрямую, как timedelta python . Это должно означать, что вместо того, чтобы искать timedelta объекта F в целочисленном поле, теоретически можно использовать DurationField, и тогда объект F вообще не должен быть в timedelta. К сожалению, из-за зависимостей я в настоящее время не могу обновить свой проект до 1.8 и проверить эту теорию.

Если кто-то еще сталкивается с этой проблемой и смогли протестировать мое предложение, я хотел бы знать. если я разрешите мои зависимости и можете обновить до 1.8, тогда я обязательно опубликую свои результаты.


Это зависит от используемого вами бэкэнда базы данных, который кажется 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"))

для расчета разницы дат в других бэкэндах базы данных см. Datediff функция, созданная Майклом Бруксом.