Jak filtrować obiekty zapytań według zakresu dat w Django?


248

Mam pole w jednym modelu, takim jak:

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

Teraz muszę filtrować obiekty według zakresu dat.

Jak filtrować wszystkie obiekty z datą pomiędzy 1-Jan-2011i 31-Jan-2011?

Odpowiedzi:


411

Posługiwać się

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

Lub jeśli próbujesz po prostu filtrować miesiąc:

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

Edytować

Jak powiedział Bernhard Vallant, jeśli chcesz zestawu zapytań wykluczającego specified range ends, powinieneś rozważyć jego rozwiązanie , które wykorzystuje gt / lt (większe niż / mniejsze niż).


Jaki typ danych date1? Mam teraz obiekt data-godzina.
user469652

8
@dcordjer: Additinally należy powiedzieć, że __rangeobejmuje granice (jak na SQL BETWEEN), jeśli nie chcesz granice włączone trzeba by iść z mojego GT / rozwiązanie lt ...
Bernhard Vallant

Czy jest to z natury posortowane w dowolnej kolejności? Jeśli tak, to które zamówienie? Dzięki.
Richard Dunn,

1
@RichardDunn Kolejność będzie oparta na domyślnej kolejności twojego modelu, lub jeśli użyjesz order_byponad wygenerowane QuerySetprzez wyżej wymienione filter. Nie używałem Django od lat.
crodjer

dla date__range musisz wstawić 01 następnego miesiąca. Oto link do dokumentacji exmaplin, która tłumaczy na 00: 00: 00.0000 dat, dlatego nie uwzględniono ostatniego dnia w twoim zakresie. docs.djangoproject.com/en/1.10/ref/models/querysets/#range w tym przypadku używam: date__range = ["% s-% s-1"% (rok, miesiąc), "% s-% s- 1 "% (rok, int (miesiąc) +1)]
SpiRail

195

Możesz używać djangofilter z datetime.dateobiektami :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))

aby uzyskać wszystko, w tym dzień 1 i 31, będziemy musieli użyć gte, prawda?
Sam Stoelinga

1
Zaletą korzystania z tej metody w porównaniu z crodjer jest to, że można przekazywać jej obiekty datetime zamiast ciągów.
Brian Kung,

79

Robiąc zakresy django z filtrem upewnij się, że znasz różnicę między użyciem obiektu daty a obiektem daty i godziny. __zakres obejmuje daty, ale jeśli użyjesz obiektu datetime dla daty końcowej, nie będzie zawierał wpisów dla tego dnia, jeśli czas nie zostanie ustawiony.

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

zwraca wszystkie wpisy od daty początkowej do końcowej, w tym pozycje z tych dat. Zły przykład, ponieważ zwraca on wpisy w tydzień w przyszłości, ale dostajesz dryf.

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

będą brakować 24-godzinne wpisy, w zależności od ustawionego czasu dla pól daty.


5
Myślę, że ważne jest, aby pamiętać, jak zaimportować dateobiekt: >>> from datetime import date >>> startdate = date.today()
Alex Spencer

19

Można obejść problem „niedopasowania impedancji” spowodowanego brakiem precyzji w DateTimeField/dateporównywaniu obiektów - który może wystąpić w przypadku użycia zakresu - za pomocą datetime.timedelta, aby dodać dzień do ostatniej daty w zakresie. Działa to jak:

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

Jak wspomniano wcześniej, bez robienia czegoś takiego, rekordy są ignorowane ostatniego dnia.

Edytowane, aby uniknąć użycia datetime.combine- wydaje się bardziej logiczne, aby trzymać się instancji daty podczas porównywania z DateTimeField, zamiast bawić się w rzucane (i mylące) datetimeobiekty. Zobacz dalsze wyjaśnienia w komentarzach poniżej.


1
Istnieje niesamowita biblioteka Delorean, która upraszcza to dzięki metodzie obcinania: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer

@tojjer: wygląda obiecująco, w jaki sposób jednak wykorzystujemy tutaj metodę obcinania?
eugene

@eugene: Właśnie zbadałem to teraz, po tylu miesiącach, i masz rację, że to naprawdę nie pomaga w tej sytuacji. Jedyny sposób, w jaki mogę to wymyślić, jest taki, jak zasugerowałem w mojej pierwotnej odpowiedzi, który polega na dostarczeniu dodatkowego „wypełnienia” do porównania z polem modelu daty i godziny, gdy filtrujesz według instancji daty. Można to zrobić za pomocą metody datetime.combine jak wyżej, ale odkryłem, że nieco łatwiej jest po prostu uwzględnić rozbieżność, dodając timedelta (dni = 1) do daty początkowej / końcowej w zakresie - - w zależności od problemu.
trojjer

Więc Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])nie będzie to obiekty utworzone na date(2014, 2, 1), jak @cademan wyjaśnił usłużnie. Ale jeśli zwiększysz datę końcową, dodając jeden dzień, otrzymasz zestaw zapytań obejmujący brakujące obiekty (i wygodnie pominiesz obiekty utworzone na podstawie date(2014, 2, 2)tego samego dziwactwa). Irytujące jest to, że określony „manualny” zakres created__gte ... created__lte=date(2014, 2, 1)również nie działa, co zdecydowanie jest sprzeczne z intuicją IMHO.
trojjer

1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] działa dla mnie
eugene

1

Jest proste,

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

Pracuje dla mnie


3
To zadziałało również dla mnie, dla noobów dla przejrzystości: (data__data = ...) oznacza ({okolwiekKolumnaDataIsCalled} __ data)
Ryan Dines

2
OP poprosił jednak o podanie zakresu
aliasav

1

Aby uczynić go bardziej elastycznym, możesz zaprojektować FilterBackend w następujący sposób:

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

-2

Nadal aktualne dzisiaj. Możesz także:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.