Come filtrare gli oggetti query per intervallo di date in Django?


248

Ho un campo in un modello come:

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

Ora, devo filtrare gli oggetti in base a un intervallo di date.

Come filtrare tutti gli oggetti che hanno una data tra 1-Jan-2011e 31-Jan-2011?

Risposte:


411

Uso

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

O se stai solo cercando di filtrare il mese saggio:

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

modificare

Come ha detto Bernhard Vallant, se si desidera un queryset che lo esclude specified range endssi dovrebbe considerare la sua soluzione , che utilizza gt / lt (maggiore di / minore di).


Qual è il tipo di dati di date1? Ho un oggetto datetime ora.
user469652,

8
@dcordjer: Inoltre, si dovrebbe dire che __rangeinclude i bordi (come sql BETWEEN), se non si desidera includere i bordi, si dovrebbe andare con la mia soluzione gt / lt ...
Bernhard Vallant,

Questo è intrinsecamente ordinato in qualche ordine? In tal caso, quale ordine? Grazie.
Richard Dunn,

1
@RichardDunn L'ordinamento si baserà sull'ordinamento predefinito del modello o se si utilizza order_byoltre il generato QuerySetdal sopra menzionato filter. Non uso Django da anni.
Crodjer,

per date__range devi inserire 01 del mese successivo. Ecco un link al documentaion che illustra in modo esplicito che traduce in 00: 00: 00.0000 delle date, quindi l'ultimo giorno nel tuo intervallo non è incluso. docs.djangoproject.com/en/1.10/ref/models/querysets/#range in questo caso uso: date__range = ["% s-% s-1"% (anno, mese), "% s-% s- 1 "% (anno, int (mese) +1)]
SpiRail

195

Puoi usare djangofilter con datetime.dateoggetti :

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

per ottenere tutto, compresi i giorni 1 e 31, dovremo usare gte giusto?
Sam Stoelinga,

1
Un vantaggio dell'utilizzo di questo metodo su crodjer è che puoi passargli oggetti datetime anziché stringhe.
Brian Kung,

79

Quando esegui intervalli django con un filtro, assicurati di conoscere la differenza tra l'utilizzo di un oggetto data e un oggetto datetime. __range è inclusivo per le date ma se si utilizza un oggetto datetime per la data di fine non includerà le voci per quel giorno se l'ora non è impostata.

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

restituisce tutte le voci da startdate a enddate comprese le voci in quelle date. Cattivo esempio dal momento che questo sta restituendo voci una settimana nel futuro, ma ottieni la deriva.

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

mancheranno le voci per 24 ore a seconda dell'ora impostata per i campi della data.


5
Penso che sia importante notare come importare un dateoggetto: >>> from datetime import date >>> startdate = date.today()
Alex Spencer,

19

È possibile aggirare la "mancata corrispondenza dell'impedenza" causata dalla mancanza di precisione nel DateTimeField/dateconfronto degli oggetti - che può verificarsi se si utilizza l' intervallo - utilizzando un datetime.timedelta per aggiungere un giorno all'ultima data nell'intervallo. Funziona come:

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

Come discusso in precedenza, senza fare qualcosa del genere, i record vengono ignorati l'ultimo giorno.

Modificato per evitare l'uso di datetime.combine- sembra più logico attenersi alle istanze di data quando si confronta con un DateTimeField, invece di fare casini con datetimeoggetti usa e getta (e confusi) . Vedi ulteriori spiegazioni nei commenti qui sotto.


1
C'è una fantastica libreria Delorean che semplifica questo con un metodo di troncamento: delorean.readthedocs.org/en/latest/quickstart.html#trunc
trojjer

@tojjer: sembra promettente, come possiamo usare qui il metodo troncato?
Eugene,

@eugene: l'ho esplorato di nuovo proprio ora, dopo tutti quei mesi, e hai ragione nel dire che non aiuta davvero in questa situazione, dopo tutto. L'unico modo per evitarlo è quello suggerito nella mia risposta originale, che è quello di fornire il "riempimento" extra per il confronto con un campo modello data / ora quando si filtra in base a un'istanza di data. Questo può essere fatto tramite il metodo datetime.combine come sopra, ma ho scoperto che può essere un po 'più semplice semplicemente soddisfare la discrepanza aggiungendo un timedelta (giorni = 1) alla data di inizio / fine dell'intervallo - - a seconda del problema.
trojjer,

Quindi Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])non includerebbe gli oggetti creati su date(2014, 2, 1), come ha spiegato utile @cademan. Ma se aumentassi la data di fine aggiungendo un giorno, otterrai un queryset che copre quegli oggetti mancanti (e omettendo convenientemente gli oggetti creati a date(2014, 2, 2)causa della stessa stranezza). La cosa fastidiosa qui è che created__gte ... created__lte=date(2014, 2, 1)neanche una gamma 'manuale' specificata con funziona, il che è sicuramente IMHO contro-intuitivo.
trojjer,

1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] funziona per me
eugene

1

È semplice,

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

Per me va bene


3
Questo ha funzionato anche per me, per i rumori per chiarezza: (date__date = ...) significa ({whateverColumnTheDateIsCalled} __ date)
Ryan Dines

2
OP ha richiesto un intervallo comunque
aliasav

1

Per renderlo più flessibile, puoi progettare un FilterBackend come di seguito:

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

Ancora rilevante oggi. Puoi anche fare:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.