Як я можу відфільтрувати дату DateTimeField у Django?


173

Я намагаюся відфільтрувати DateTimeFieldпорівняння з датою. Я маю на увазі:

MyObject.objects.filter(datetime_attr=datetime.date(2009,8,22))

Я отримую порожній список запитів як відповідь, тому що (я думаю) я не розглядаю час, але хочу "будь-який час".

Чи є у Джанго простий спосіб зробити це?

У мене встановлений час у встановленому даті, це не так 00:00.


5
Це одне з прикрощів Джанго. Зважаючи на те, що це простий і поширений випадок використання, не існує простого способу цього досягти.
rubayeet

Відповіді:


114

Такі пошуку реалізуються django.views.generic.date_basedтаким чином:

{'date_time_field__range': (datetime.datetime.combine(date, datetime.time.min),
                            datetime.datetime.combine(date, datetime.time.max))} 

Оскільки це досить багатослівно, є плани покращити синтаксис за допомогою __dateоператора. Щоб отримати детальнішу інформацію, поставте прапорець " # 9596 Порівнюючи DateTimeField із датою занадто важко ".


4
Використання з дальністю: Q(created__gte=datetime.combine(created_value, time.min))
Дінго

10
Схоже, він приземлиться у Django 1.9: github.com/django/django/commit/…
amjoconn

14
Нове в Django 1.9:Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Non

102
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// редагувати після коментарів

YourModel.objects.filter(datetime_published=datetime(2008, 03, 27))

не працює, оскільки він створює об'єкт дати з значеннями часу, встановленими на 0, тому час у базі даних не збігається.


THX для відповіді! перша альтернатива не працює з полями timetime. Друга альтернатива працює;). Якщо хтось знає інший метод, будь ласка, дайте відповідь
Xidobix

docs.python.org/library/datetime.html#datetime-objects, що використовують datetime () від модуля datetime hrs, min, secs є необов’язковим. друге - з робочого проекту із заміненими vars, ви можете подивитися у документах це правильно
zalew

я знаю, що це необов’язково, проблема полягає в тому, що в моєму полі дат встановлено час, це не 00:00
Xidobix

"перша альтернатива не працює з полями дат." це буде досить дивно, оскільки datetime.datetime () повертає об'єкт datetime djangoproject.com/documentation/0.96/models/basic перевірити визначення моделі та приклади: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew

"Я знаю, що це необов'язково. Проблема полягає в тому, що для мого дату встановлено час, встановлений час, це не 00:00" О, зараз я це розумію. Так, без аргументів у часі він встановлюється на 00, тому він не повертається :)
zalew

88

Ось результати, отримані за допомогою функції timeit ipython:

from datetime import date
today = date.today()

timeit[Model.objects.filter(date_created__year=today.year, date_created__month=today.month, date_created__day=today.day)]
1000 loops, best of 3: 652 us per loop

timeit[Model.objects.filter(date_created__gte=today)]
1000 loops, best of 3: 631 us per loop

timeit[Model.objects.filter(date_created__startswith=today)]
1000 loops, best of 3: 541 us per loop

timeit[Model.objects.filter(date_created__contains=today)]
1000 loops, best of 3: 536 us per loop

містить, здається, швидше.


Це рішення, здається, є останнім часом. Я здивований, що він отримав 4 оновлення, тому що, коли я спробую containsрішення, я отримую повідомлення про помилку:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Хоуман

Сьогодні я повторно перевіряю та оновлюю результати, і не думаю, що ваша помилка викликана __containsфільтром. Але якщо у вас виникають проблеми, ви повинні спробувати приклад документів django, який використовується __gte.
Морено

4
Метод __contains працює для мене чудово. Я думаю, що це, мабуть, найкраща відповідь, оскільки він дає порівняння ефективності. Я проголосував більше одного, але я здивований, що в ньому немає більшої кількості голосів.
RobotHumans

Я люблю startswithрішення ..
w411 3

46

Тепер у Django є __date фільтр набору запитів для запиту об'єктів datetime проти дат у версії розробки. Таким чином, він буде доступний незабаром в 1.9.


Так, це було додано в 1.9 docs.djangoproject.com/en/1.9/ref/models/querysets/#date
guival

Найкраща відповідь, дійсна для новіших версій Джанго
serfer2

Це не працює для мене, не маю уявлення, чому :( Я перебуваю на django 1.11. Моє точне виключення: NotImplementedError: для підкласів базових данихоперацій може знадобитися метод datetime_cast_date ()
AbdurRehman Khan

44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

вище - це те, що я використав. Це не тільки працює, але й має певну логічну підтримку.


3
Набагато краще, ніж на всі інші відповіді тут, дякую!
Кін

sssss-shazam! 💯
дпс

30

Щодо Django 1.9, спосіб зробити це - за допомогою __dateоб'єкта datetime.

Наприклад: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))


27

Це дає ті самі результати, що і __year, __month та __day, і, здається, працює для мене:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))

19
виглядає так, що цей перетворює об’єкт дати в рядок і виконує порівняння рядків дат, тому змушує db сканувати повну таблицю. для великих столів цей вбиває ваш виступ
yilmazhuseyin

8

припустимо, що active_on є об'єктом дати, збільшуючи його на 1 день, тоді зробіть діапазон

next_day = active_on + datetime.timedelta(1)
queryset = queryset.filter(date_created__range=(active_on, next_day) )

2

Ось цікава методика - я скористався процедурою startwith, що реалізовується разом із Django на MySQL, щоб досягти результату лише пошуку дати через лише дату. В основному, коли Django здійснює пошук у базі даних, він повинен здійснити перетворення рядків для об'єкта зберігання DATETIME MySQL, тож ви можете фільтрувати по ньому, залишаючи поза часовою міткою дату - таким чином% LIKE% відповідає лише даті об'єкта, і ви отримуватимете кожну часову позначку на дану дату.

datetime_filter = datetime(2009, 8, 22) 
MyObject.objects.filter(datetime_attr__startswith=datetime_filter.date())

Це виконає такий запит:

SELECT (values) FROM myapp_my_object \ 
WHERE myapp_my_object.datetime_attr LIKE BINARY 2009-08-22%

У цьому випадку LIKE BINARY відповідає всім датам, незалежно від часової позначки. Включаючи такі значення, як:

+---------------------+
| datetime_attr       |
+---------------------+
| 2009-08-22 11:05:08 |
+---------------------+

Сподіваємось, це допомагає всім, поки Джанго не вийде з рішенням!


Гаразд, так це здається такою ж відповіддю, що і mhost та kettlehell вище, але з більш детальним описом того, що відбувається в бекенді. Принаймні, у вас є підстава використовувати атрибут містить або startwith разом з атрибутом date () datetime!
bbengfort

2

Тут є фантастичний блог, який висвітлює це: Порівняння дат та дат у ORG Django

Найкраще рішення, розміщене для Django> 1.7, <1.9, - це зареєструвати перетворення:

from django.db import models

class MySQLDatetimeDate(models.Transform):
    """
    This implements a custom SQL lookup when using `__date` with datetimes.
    To enable filtering on datetimes that fall on a given date, import
    this transform and register it with the DateTimeField.
    """
    lookup_name = 'date'

    def as_sql(self, compiler, connection):
        lhs, params = compiler.compile(self.lhs)
        return 'DATE({})'.format(lhs), params

    @property
    def output_field(self):
        return models.DateField()

Тоді ви можете використовувати його у своїх фільтрах так:

Foo.objects.filter(created_on__date=date)

EDIT

Це рішення, безумовно, залежить від зворотного кінця. Зі статті:

Звичайно, ця реалізація покладається на ваш особливий смак SQL, який має функцію DATE (). MySQL робить. Так само і SQLite. З іншого боку, я особисто не працював з PostgreSQL, але деякий googling приводить мене до думки, що він не має функції DATE (). Отже, реалізація цієї простої здається, що вона обов'язково буде дещо залежною.


це специфічно для MySQL? цікаво про вибір назви класу.
Binoj David

@BinojDavid Так, це залежить від бекенда
Ден Гейл,

Я протестував його за допомогою Django 1.8 та PostgreSQL 9.6.6, і він працює чудово. Насправді, використовуючи PostgreSQL, ви також можете використовувати цей синтаксис:return '{}::date'.format(lhs), params
michal-michalak



0

У Django 1.7.6 працює:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))

2
Це працює лише в тому випадку, якщо ви шукаєте точну дату, наприклад, ви не можете використовувати __lte.
відвід

-1

Дивіться статтю Документація Джанго

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))

5
в документації на джанго вона працює, тому що дату встановленого часу має час 00:00
Xidobix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.