Jak mogę filtrować datę DateTimeField w Django?

173

Próbuję odfiltrować DateTimeFieldporównanie z datą. Mam na myśli:

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

Otrzymuję pustą listę zestawów zapytań jako odpowiedź, ponieważ (myślę) nie biorę pod uwagę czasu, ale chcę „kiedykolwiek”.

Czy w Django jest na to łatwy sposób?

Mam ustawiony czas w dacie i czasie, tak nie jest 00:00.

Xidobix
źródło
5
To jedna z irytacji Django. Biorąc pod uwagę, że jest to prosty i powszechny przypadek użycia, nie ma prostego sposobu na osiągnięcie tego.
rubayeet,

Odpowiedzi:

114

Takie wyszukiwania są realizowane w django.views.generic.date_basednastępujący sposób:

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

Ponieważ jest dość rozwlekły, planowane jest ulepszenie składni za pomocą __dateoperatora. Sprawdź „ # 9596 Porównywanie pola DateTimeField z datą jest zbyt trudne ”, aby uzyskać więcej informacji.

Piotr Czapla
źródło
4
Używanie z zakresem: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
Wygląda na to, że wyląduje w Django 1.9: github.com/django/django/commit/ ...
amjoconn
14
Nowość w 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')

// edytuj po komentarzach

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

nie działa, ponieważ tworzy obiekt daty i godziny z wartościami czasu ustawionymi na 0, więc czas w bazie danych nie jest zgodny.

zalew
źródło
dzięki za odpowiedź! pierwsza alternatywa nie działa z polami datetimefield. Druga alternatywa działa;). Jeśli ktoś zna inną metodę, proszę o odpowiedź
Xidobix
docs.python.org/library/datetime.html#datetime-objects przy użyciu funkcji datetime () z modułu datetime hrs, mins, secs jest opcjonalne. drugi to z projektu pracy z Vars zastąpiony, można szukać w docs to poprawne
Zalew
wiem, że to opcjonalne, problem polega na tym, że moje pole datetimefield ma ustawiony czas, nie jest to 00:00
Xidobix
„Pierwsza alternatywa nie działa z polami danych”. byłoby to dość zaskakujące, ponieważ datetime.datetime () zwraca obiekt datetime djangoproject.com/documentation/0.96/models/basic sprawdź definicję modelu i przykłady: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew
„Wiem, że jest to opcjonalne, problem polega na tym, że moje pole daty ma ustawiony czas, nie jest to 00:00” Och, teraz rozumiem. Tak, bez argumentów time ustawia na 00, więc nie zwraca :)
zalew
88

Oto wyniki, które otrzymałem dzięki funkcji timeit ipythona:

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

zawiera wydaje się być szybszy.

Moreno
źródło
To rozwiązanie wydaje się być najnowsze. Dziwię się, że dostałem 4 głosy poparcia, bo jak próbuję containsrozwiązania to wyskakuje mi komunikat o błędzie:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Dzisiaj ponownie sprawdzam i aktualizuję wyniki i nie sądzę, że twój błąd jest spowodowany przez __containsfiltr. Ale jeśli napotkasz problemy, powinieneś wypróbować przykład django docs, który używa __gte.
Moreno,
4
U mnie dobrze działa metoda __contains. Myślę, że jest to prawdopodobnie najlepsza odpowiedź, ponieważ zapewnia porównania wydajności. Zagłosowałem więcej niż jeden, ale jestem zaskoczony, że nie ma więcej głosów za.
RobotHumans
Uwielbiam to startswithrozwiązanie…
w411 3
46

Teraz Django ma filtr __date queryset do wykonywania zapytań dotyczących obiektów datetime względem dat w wersji rozwojowej. W związku z tym wkrótce będzie dostępny w wersji 1.9.

onurmatik
źródło
Najlepsza odpowiedź, ważna dla nowszych wersji Django
serfer2
To nie działa dla mnie, nie mam pojęcia dlaczego :( Jestem na django 1.11 Mój dokładny wyjątek to: NotImplementedError: podklasy operacji opartych na bazie danych mogą wymagać metody datetime_cast_date ()
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

powyższe jest tym, czego użyłem. Nie tylko działa, ale ma również nieodłączne logiczne podłoże.

czajnik
źródło
3
O wiele lepiej niż wszystkie inne odpowiedzi tutaj, dzięki!
Kin,
sssss-shazam! 💯
dps
30

Od wersji Django 1.9 sposobem na to jest użycie __date obiektu datetime.

Na przykład: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))

Andrew B.
źródło
27

Daje to takie same wyniki, jak użycie __year, __month i __day i wydaje się działać dla mnie:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
źródło
19
wygląda na to, że ten zamienia obiekt daty na łańcuch i wykonuje ciągowe porównanie dat, dlatego zmusza db do pełnego skanowania tabeli. w przypadku dużych stołów ten jeden zabija Twój występ
yilmazhuseyin,
8

zakładając, że active_on jest obiektem daty, zwiększ go o 1 dzień, a następnie wykonaj zakres

next_day = active_on + datetime.timedelta(1)
queryset = queryset.filter(date_created__range=(active_on, next_day) )
davidj411
źródło
2

Oto interesująca technika - wykorzystałem procedurę startedwith zaimplementowaną w Django w MySQL, aby uzyskać wynik wyszukiwania tylko daty i godziny tylko przez datę. Zasadniczo, gdy Django szuka w bazie danych, musi wykonać konwersję ciągów znaków dla obiektu pamięci MySQL DATETIME, więc możesz filtrować według tego, pomijając część daty z datownikiem - w ten sposób% LIKE% pasuje tylko do daty obiekt, a otrzymasz każdy znacznik czasu dla podanej daty.

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

Spowoduje to wykonanie następującego zapytania:

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

LIKE BINARY w tym przypadku dopasuje wszystko do daty, bez względu na sygnaturę czasową. W tym wartości takie jak:

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

Miejmy nadzieję, że pomoże to wszystkim, dopóki Django nie wyjdzie z rozwiązaniem!

bbengfort
źródło
Ok, więc wydaje się, że to ta sama odpowiedź, co powyżej mhost i boilerhell, ale z dokładniejszym opisem tego, co dzieje się w backendzie. Przynajmniej masz powód, aby używać zawiera lub zaczyna się wraz z atrybutem date () datetime!
bbengfort
2

Jest fantastyczny post na blogu, który omawia to tutaj: Porównanie dat i czasów w ORM Django

Najlepszym rozwiązaniem opublikowanym dla Django> 1.7, <1.9 jest zarejestrowanie transformacji:

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()

Następnie możesz użyć go w swoich filtrach w następujący sposób:

Foo.objects.filter(created_on__date=date)

EDYTOWAĆ

To rozwiązanie jest zdecydowanie zależne od zaplecza. Z artykułu:

Oczywiście ta implementacja opiera się na konkretnej wersji SQL zawierającej funkcję DATE (). MySQL tak. Podobnie SQLite. Z drugiej strony nie pracowałem osobiście z PostgreSQL, ale niektóre google prowadzą mnie do przekonania, że ​​nie ma on funkcji DATE (). Tak więc implementacja tak prosta wydaje się być z konieczności zależna od zaplecza.

Dan Gayle
źródło
czy to jest specyficzne dla MySQL? zastanawiasz się nad wyborem nazwy klasy.
Binoj David
@BinojDavid Tak, jest zależny od zaplecza
Dan Gayle,
Przetestowałem go używając Django 1.8 i PostgreSQL 9.6.6 i działa doskonale. Właściwie używając PostgreSQL możesz również użyć tej składni:return '{}::date'.format(lhs), params
michal-michalak
1

Hm ... Moje rozwiązanie działa:

Mymodel.objects.filter(date_time_field__startswith=datetime.datetime(1986, 7, 28))
satelity
źródło
1
Model.objects.filter(datetime__year=2011, datetime__month=2, datetime__day=30)
Skylar Saveland
źródło
0

W Django 1.7.6 działa:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
źródło
2
Działa to tylko wtedy, gdy szukasz dokładnej daty, której nie możesz użyć __ltena przykład.
guival
-1

Zobacz artykuł Dokumentacja Django

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
źródło
5
w dokumentacji django to działa, ponieważ
plik