Разница во времени 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, я обязательно отправлю обратно свои результаты.