Wie kann ich ein Datum eines DateTimeField in Django filtern?

173

Ich versuche einen DateTimeFieldVergleich mit einem Datum zu filtern . Ich meine:

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

Ich bekomme eine leere Abfragesatzliste als Antwort, weil ich (glaube ich) nicht über Zeit nachdenke, sondern "jederzeit" möchte.

Gibt es in Django einen einfachen Weg, dies zu tun?

Ich habe die Zeit in der Datumszeit festgelegt, es ist nicht 00:00.

Xidobix
quelle
5
Dies ist einer der Ärger von Django. Da dies ein einfacher und häufiger Anwendungsfall ist, gibt es keinen einfachen Weg, dies zu erreichen.
Rubayeet
Siehe auch
MackM

Antworten:

113

Solche Suchvorgänge werden django.views.generic.date_basedwie folgt implementiert :

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

Da es ziemlich ausführlich ist, gibt es Pläne, die Syntax mit dem __dateOperator zu verbessern . Weitere Informationen finden Sie unter " # 9596 Das Vergleichen eines DateTimeField mit einem Datum ist zu schwierig ".

Piotr Czapla
quelle
4
Verwendung mit Reichweite: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
Sieht so aus, als würde es in Django 1.9 landen: github.com/django/django/commit/…
amjoconn
14
Neu in Django 1.9:Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Nicht
102
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// nach Kommentaren bearbeiten

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

funktioniert nicht, da ein datetime-Objekt mit auf 0 gesetzten Zeitwerten erstellt wird, sodass die Zeit in der Datenbank nicht übereinstimmt.

zalew
quelle
Danke für die Antwort! Die erste Alternative funktioniert nicht mit datetimefields. Die zweite Alternative funktioniert;). Wenn jemand eine andere Methode kennt, antworten Sie bitte
Xidobix
docs.python.org/library/datetime.html#datetime-objects mit datetime () aus dem datetime-Modul hrs, min, secs ist optional. Das zweite stammt aus einem Arbeitsprojekt, bei dem vars ersetzt wurde. Sie können in den Dokumenten nachsehen, ob es korrekt ist
zalew
Ich weiß, es ist optional, das Problem ist, dass mein Datum / Uhrzeit-Feld die Zeit eingestellt hat, es ist nicht 00:00
Xidobix
"Die erste Alternative funktioniert nicht mit Datetime-Feldern." Es wäre ziemlich überraschend, da datetime.datetime () ein datetime-Objekt zurückgibt. djangoproject.com/documentation/0.96/models/basic Überprüfen Sie die Modelldefinition und Beispiele: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
Zalew
"Ich weiß, dass es optional ist. Das Problem ist, dass mein Datum / Uhrzeit-Feld die Zeit eingestellt hat. Es ist nicht 00:00." Oh, jetzt verstehe ich es. Ja, ohne Zeitargumente wird es auf 00 gesetzt, daher wird es nicht zurückgegeben :)
zalew
88

Hier sind die Ergebnisse, die ich mit der Timeit-Funktion von ipython erhalten habe:

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

enthält scheint schneller zu sein.

Moreno
quelle
Diese Lösung scheint die neueste zu sein. Ich bin überrascht, dass es 4 positive Stimmen bekommen hat, denn wenn ich die containsLösung versuche , erhalte ich die Fehlermeldung:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Ich überprüfe und aktualisiere die Ergebnisse heute erneut und glaube nicht, dass Ihr Fehler durch den __containsFilter verursacht wird . Aber wenn Sie in Probleme laufen sollten Sie versuchen django docs Beispiel , die verwendet __gte.
Moreno
4
Die __contains-Methode funktioniert gut für mich. Ich denke, dies ist wahrscheinlich die beste Antwort, da es Leistungsvergleiche bietet. Ich habe mehr als eine Stimme abgegeben, aber ich bin überrascht, dass es nicht mehr positive Stimmen gibt.
RobotHumans
Ich liebe die startswithLösung ..
w411 3
46

Jetzt verfügt Django über einen __date-Abfragesatzfilter, um datetime-Objekte anhand von Datumsangaben in der Entwicklungsversion abzufragen. Somit wird es bald in 1.9 verfügbar sein.

onurmatik
quelle
Beste Antwort, gültig für neuere Django-Versionen
serfer2
Dies funktioniert bei mir nicht, keine Ahnung warum :( Ich bin auf Django 1.11. Meine genaue Ausnahme ist: NotImplementedError: Unterklassen von basedatabaseoperations erfordern möglicherweise eine datetime_cast_date () -Methode
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

Das Obige habe ich benutzt. Es funktioniert nicht nur, es hat auch einen inhärenten logischen Hintergrund.

Kettlehell
quelle
3
Viel besser als alle anderen Antworten hier, danke!
Kin
sssss-shazam! 💯
dps
30

Ab Django 1.9 können Sie dazu __dateein datetime-Objekt verwenden.

Beispielsweise: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))

Andrew B.
quelle
27

Dies führt zu den gleichen Ergebnissen wie die Verwendung von __Jahr, __Monat und __Tag und scheint für mich zu funktionieren:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
quelle
19
Es sieht so aus, als würde dieses Datumsobjekt in eine Zeichenfolge umgewandelt und ein Zeichenfolgenvergleich der Daten durchgeführt. Daher wird db gezwungen, einen vollständigen Tabellenscan durchzuführen. für große Tische dieses töten Ihre Leistung
yilmazhuseyin
8

Angenommen, active_on ist ein Datumsobjekt, erhöhen Sie es um 1 Tag und führen Sie dann den Bereich aus

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

Hier ist eine interessante Technik: Ich habe die mit Django unter MySQL implementierte Startswith-Prozedur genutzt, um das Ergebnis zu erzielen, dass nur eine Datums- / Uhrzeitangabe nur über das Datum nachgeschlagen wird. Wenn Django die Suche in der Datenbank durchführt, muss grundsätzlich eine Zeichenfolgenkonvertierung für das DATETIME MySQL-Speicherobjekt durchgeführt werden, damit Sie danach filtern können, wobei der Zeitstempelteil des Datums weggelassen wird. Auf diese Weise stimmt% LIKE% nur mit dem Datum überein Objekt und Sie erhalten jeden Zeitstempel für das angegebene Datum.

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

Dadurch wird die folgende Abfrage ausgeführt:

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

In diesem Fall entspricht LIKE BINARY dem Datum, unabhängig vom Zeitstempel. Einschließlich Werte wie:

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

Hoffentlich hilft das allen, bis Django eine Lösung findet!

bbengfort
quelle
Ok, dies scheint also die gleiche Antwort zu sein wie mhost und kettlehell oben, aber mit einer genaueren Beschreibung dessen, was im Backend passiert. Zumindest haben Sie einen Grund, enthält zusammen mit dem date () -Attribut von datetime!
Bbengfort
2

Es gibt einen fantastischen Blogpost, der dies hier behandelt: Vergleichen von Daten und Daten im Django ORM

Die beste Lösung für Django> 1.7, <1.9 ist das Registrieren einer Transformation:

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

Dann können Sie es in Ihren Filtern wie folgt verwenden:

Foo.objects.filter(created_on__date=date)

BEARBEITEN

Diese Lösung ist definitiv vom Backend abhängig. Aus dem Artikel:

Diese Implementierung hängt natürlich von Ihrer speziellen SQL-Variante mit einer DATE () -Funktion ab. MySQL tut es. SQLite auch. Andererseits habe ich nicht persönlich mit PostgreSQL gearbeitet, aber ein bisschen googeln lässt mich glauben, dass es keine DATE () -Funktion hat. Eine so einfache Implementierung scheint also notwendigerweise etwas backendabhängig zu sein.

Dan Gayle
quelle
Ist das MySQL-spezifisch? Ich frage mich über die Wahl des Klassennamens.
Binoj David
@ BinojDavid Ja, es ist Backend-abhängig
Dan Gayle
Ich habe es mit Django 1.8 und PostgreSQL 9.6.6 getestet und es funktioniert perfekt. Mit PostgreSQL können Sie auch folgende Syntax verwenden:return '{}::date'.format(lhs), params
michal-michalak
1

Hm .. Meine Lösung funktioniert:

Mymodel.objects.filter(date_time_field__startswith=datetime.datetime(1986, 7, 28))
Satelliten
quelle
1
Model.objects.filter(datetime__year=2011, datetime__month=2, datetime__day=30)
Skylar Saveland
quelle
0

In Django 1.7.6 funktioniert:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
quelle
2
Dies funktioniert nur, wenn Sie nach dem genauen Datum suchen, das Sie __ltebeispielsweise nicht verwenden können .
Guival
-1

Siehe den Artikel Django-Dokumentation

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
quelle
5
In der Django-Dokumentation funktioniert es, weil die datetime-Datei die Zeit 00:00
Xidobix