Wie filtere ich Abfrageobjekte in Django nach Datumsbereich?

248

Ich habe ein Feld in einem Modell wie:

class Sample(models.Model):
    date = fields.DateField(auto_now=False)

Jetzt muss ich die Objekte nach einem Datumsbereich filtern.

Wie filtere ich alle Objekte, deren Datum zwischen 1-Jan-2011und liegt 31-Jan-2011?

user469652
quelle

Antworten:

409

Verwenden

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

Oder wenn Sie nur versuchen, monatlich zu filtern:

Sample.objects.filter(date__year='2011', 
                      date__month='01')

Bearbeiten

Wie Bernhard Vallant sagte, sollten Sie, wenn Sie ein Abfrageset wünschen, das das ausschließt, seine Lösung inspecified range ends Betracht ziehen , die gt / lt (größer als / kleiner als) verwendet.

Crodjer
quelle
Was ist der Datentyp von date1? Ich habe jetzt ein Datum / Uhrzeit-Objekt.
user469652
8
@dcordjer: Zusätzlich sollte gesagt werden, dass __rangedie Ränder (wie SQLs BETWEEN) enthalten, wenn Sie nicht möchten, dass die Ränder enthalten sind, müssten Sie mit meiner gt / lt-Lösung gehen ...
Bernhard Vallant
Ist das von Natur aus in beliebiger Reihenfolge sortiert? Wenn ja, welche Bestellung? Vielen Dank.
Richard Dunn
1
@RichardDunn Die Reihenfolge Standard Ihres Modells basieren auf Bestellung, oder wenn Sie order_bydie erzeugte über QuerySetdurch die oben genannten filter. Ich habe Django seit Jahren nicht mehr benutzt.
Crodjer
Für date__range müssen Sie 01 des nächsten Monats eingeben. Hier ist ein Link zu der Dokumentation, die zeigt, dass sie in 00: 00: 00.0000 der Daten übersetzt wird. Daher ist der letzte Tag in Ihrem Bereich nicht enthalten. docs.djangoproject.com/de/1.10/ref/models/querysets/#range In diesem Fall verwende ich: date__range = ["% s-% s-1"% (Jahr, Monat), "% s-% s- 1 "% (Jahr, int (Monat) +1)]
SpiRail
195

Sie können Djangosfilter mit datetime.dateObjekten verwenden :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))
Bernhard Vallant
quelle
Um alles einschließlich Tag 1 und 31 zu bekommen, müssen wir gte verwenden, oder?
Sam Stoelinga
1
Ein Vorteil der Verwendung dieser Methode gegenüber Crodjer besteht darin, dass Sie Datetime-Objekte anstelle von Zeichenfolgen übergeben können.
Brian Kung
79

Wenn Sie Django-Bereiche mit einem Filter ausführen, stellen Sie sicher, dass Sie den Unterschied zwischen der Verwendung eines Datumsobjekts und einem Datum / Uhrzeit-Objekt kennen. __range ist an Datumsangaben inklusive. Wenn Sie jedoch ein Datums- / Uhrzeitobjekt für das Enddatum verwenden, werden die Einträge für diesen Tag nicht berücksichtigt, wenn die Uhrzeit nicht festgelegt ist.

    startdate = date.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

Gibt alle Einträge vom Startdatum bis zum Enddatum zurück, einschließlich der Einträge an diesen Daten. Schlechtes Beispiel, da dies Einträge eine Woche in der Zukunft zurückgibt, aber Sie bekommen die Drift.

    startdate = datetime.today()
    enddate = startdate + timedelta(days=6)
    Sample.objects.filter(date__range=[startdate, enddate])

Abhängig von der Uhrzeit für die Datumsfelder fehlen Einträge im Wert von 24 Stunden.

Cademan
quelle
5
Ich denke, es ist wichtig zu beachten, wie man ein dateObjekt importiert : >>> from datetime import date >>> startdate = date.today()
Alex Spencer
19

Sie können die "Impedanzfehlanpassung" umgehen , die durch die mangelnde Genauigkeit des DateTimeField/dateObjektvergleichs verursacht wird - die bei Verwendung des Bereichs auftreten kann -, indem Sie mit datetime.timedelta einen Tag zum letzten Datum im Bereich hinzufügen. Das funktioniert wie folgt:

start = date(2012, 12, 11)
end = date(2012, 12, 18)
new_end = end + datetime.timedelta(days=1)

ExampleModel.objects.filter(some_datetime_field__range=[start, new_end])

Wie bereits erwähnt, werden Datensätze am letzten Tag ignoriert, ohne so etwas zu tun.

Bearbeitet, um die Verwendung von datetime.combine- zu vermeiden. Es scheint logischer, sich beim Vergleich DateTimeFieldmit a an Datumsinstanzen zu halten , anstatt mit wegwerfbaren (und verwirrenden) datetimeObjekten herumzuspielen . Weitere Erläuterungen finden Sie in den Kommentaren unten.

trojjer
quelle
1
Es gibt eine fantastische Delorean-Bibliothek, die dies mit einer Kürzungsmethode
trojjer
@tojjer: sieht vielversprechend aus, wie verwenden wir hier jedoch die Methode zum Abschneiden?
Eugene
@eugene: Ich habe das gerade nach all den Monaten noch einmal untersucht und du hast Recht damit, dass es in dieser Situation doch nicht wirklich hilft. Der einzige Weg, den ich mir vorstellen kann, ist der in meiner ursprünglichen Antwort vorgeschlagene, der darin besteht, das zusätzliche Auffüllen zum Vergleich mit einem Datums- / Uhrzeitmodellfeld bereitzustellen, wenn Sie nach einer Datumsinstanz filtern. Dies kann wie oben beschrieben über die Methode datetime.combine erfolgen. Ich habe jedoch festgestellt, dass es etwas einfacher sein kann, die Diskrepanz nur durch Hinzufügen eines Zeitdeltas (Tage = 1) zum Start- / Enddatum im Bereich zu berücksichtigen. - je nach Problem.
Trojjer
So Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])würde auch nicht selbst erstellte Objekte auf date(2014, 2, 1), wie @cademan helfend erläutert. Wenn Sie jedoch das Enddatum um einen Tag erhöhen, erhalten Sie ein Abfrageset, das diese fehlenden Objekte abdeckt (und bequemerweise Objekte weglässt, die date(2014, 2, 2)aufgrund derselben Eigenart erstellt wurden). Das ärgerliche dabei ist, dass ein mit angegebener 'manueller' Bereich created__gte ... created__lte=date(2014, 2, 1)auch nicht funktioniert, was meiner Meinung nach definitiv kontraintuitiv ist.
Trojjer
1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] funktioniert für mich
eugene
1

Ist einfach,

YourModel.objects.filter(YOUR_DATE_FIELD__date=timezone.now())

Funktioniert bei mir

Jonhatan Fajardo
quelle
3
Dies funktionierte auch für mich, für die Noobs aus Gründen der Klarheit: (Datum__Datum = ...) bedeutet ({WhateverColumnTheDateIsCalled} __ Datum)
Ryan Dines
2
OP bat jedoch um eine Reichweite
aliasav
1

Um es flexibler zu gestalten, können Sie ein FilterBackend wie folgt entwerfen:

class AnalyticsFilterBackend(generic_filters.BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        predicate = request.query_params # or request.data for POST

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is not None:
            queryset = queryset.filter(your_date__range=(predicate['from_date'], predicate['to_date']))

        if predicate.get('from_date', None) is not None and predicate.get('to_date', None) is None:
            queryset = queryset.filter(your_date__gte=predicate['from_date'])

        if predicate.get('to_date', None) is not None and predicate.get('from_date', None) is None:
            queryset = queryset.filter(your_date__lte=predicate['to_date'])
        return queryset
saran3h
quelle
-2

Noch heute relevant. Sie können auch tun:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Bhavik Shah
quelle