Разница во времени 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
функция, созданная Майклом Бруксом.