Выполнение запросов с использованием F () и timedelta в django - PullRequest
9 голосов
/ 28 мая 2011

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

class Process(models.Model):
  title = models.Charfield(max_length=255)
  date_up = models.DateTimeField(auto_now_add=True)
  days_activation = models.PositiveSmallIntegerField(default=0)

Теперь мне нужно запросить все Process объекты, срок действия которых истек, в соответствии с их значением days_activation.

Я пытался

from datetime import datetime, timedelta

Process.objects.filter(date_up__lte=datetime.now()-timedelta(days=F('days_activation')))

и получил следующее сообщение об ошибке:

TypeError: неподдерживаемый тип для компонента timedelta days: F

Конечно, я могу сделать это на Python:

filter (lambda x: x.date_up<=datetime.now() - timedelta(days=x.days_activation), 
        Process.objects.all ()), 

, но мне действительно нужно произвести django.db.models.query.QuerySet.

Ответы [ 4 ]

1 голос
/ 22 февраля 2017

Вы должны расширить АгрегатСделайте, как показано ниже:

from django.db import models as DM

class BaseSQL(object):
    function = 'DATE_SUB'
    template = '%(function)s(NOW(), interval %(expressions)s day)'

class DurationAgr(BaseSQL, DM.Aggregate):
    def __init__(self, expression, **extra):
        super(DurationAgr, self).__init__(
            expression,
            output_field=DM.DateTimeField(),
            **extra
        )

Process.objects.filter(date_up__lte=DurationAgr('days_activation'))

Надеюсь, это будет работать для вас.:)

1 голос
/ 28 мая 2011

Вы смешиваете два слоя: слой времени выполнения и слой базы данных. Функция F - это всего лишь помощник, который позволяет вам создавать чуть более сложные запросы с помощью django ORM. Вы используете timedelta и F вместе и ожидаете, что django ORM будет достаточно умен, чтобы конвертировать эти вещи в сырой SQL, но, как я вижу, это невозможно. Может быть, я ошибаюсь и ничего не знаю о django ORM.

В любом случае, вы можете переписать свой вызов ORM с помощью дополнительных extra и создать предложение WHERE вручную, используя собственные функции SQL, что равно datetime.now() и timedelta.

0 голосов
/ 12 июля 2018

Я пытался использовать решение Лютца Пречелта выше, но получил ошибку синтаксиса MySQL. Это потому, что мы не можем выполнять арифметические операции с INTERVAL в MySQL.

Итак, для MySQL мое решение заключается в создании пользовательской функции БД:

class MysqlSubDate(Func):
    function = 'SUBDATE'
    output_field = DateField()

Пример использования:

.annotate(remainded_days=MysqlSubDate('end_datetime', F('days_activation')))

Также вы можете использовать таймделту, она будет конвертирована в ИНТЕРВАЛ

.annotate(remainded_days=MysqlSubDate('end_datetime', datetime.timedelta(days=10)))
0 голосов
/ 19 сентября 2017

7 дней == 1 день * 7

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

В вашем случае django.db.models.query.filter знает о F, а datetime.timedelta - нет. Следовательно, вам нужно не указывать F в списке аргументов timedelta. К счастью, умножение timedelta * int поддерживается F, поэтому может работать следующее:

Process.objects.filter(date_up__lte=datetime.now()-timedelta(days=1)*F('days_activation'))

Как оказалось, этот будет работать с PostgreSQL , но не будет работать с SQlite (для которого Django 1.11 поддерживает только + и - для timedelta, возможно, из-за соответствующего ограничения SQlite).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...