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

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

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-запроса?

Ответы

Ответ 1

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

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

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

Ответ 2

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

см. определение F() в django/db/models/expression.py и Node в 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')))

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

Ответ 3

На всякий случай, если кто-то еще ищет это, вот что-то, о чем стоит подумать.

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

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

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