Come posso filtrare una data di un DateTimeField in Django?


173

Sto cercando di filtrare un DateTimeFieldconfronto con una data. Intendo:

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

Ricevo un elenco di query vuoto come risposta perché (penso) non sto prendendo in considerazione il tempo, ma voglio "in qualsiasi momento".

C'è un modo semplice in Django per farlo?

Ho impostato il tempo nel datetime, non lo è 00:00.


5
Questo è uno dei fastidi di Django. Considerando che si tratta di un caso d'uso semplice e comune, non esiste un modo semplice per raggiungere questo obiettivo.
Rubayeet

Risposte:


114

Tali ricerche sono implementate django.views.generic.date_basedcome segue:

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

Poiché è abbastanza dettagliato, ci sono piani per migliorare la sintassi usando l' __dateoperatore. Controlla " # 9596 Confrontare un DateTimeField con una data è troppo difficile " per maggiori dettagli.


4
Utilizzo con intervallo: Q(created__gte=datetime.combine(created_value, time.min))
Dingo

10
Sembra che arriverà in Django 1.9: github.com/django/django/commit/…
amjoconn

14
Nuovo in 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')

// modifica dopo i commenti

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

non funziona perché crea un oggetto datetime con valori di ora impostati su 0, quindi l'ora nel database non corrisponde.


grazie per la risposta! la prima alternativa non funziona con i campi datetime. La seconda alternativa funziona;). Se qualcuno conosce un altro metodo, rispondi
Xidobix,

docs.python.org/library/datetime.html#datetime-objects utilizzando datetime () dal modulo datetime ore, minuti, secondi è facoltativo. il secondo proviene da un progetto funzionante con i vars sostituiti, puoi vedere nei documenti che è corretto
zalew,

so che è facoltativo, il problema è che il mio datetimefield ha il tempo impostato, non è 00:00
Xidobix

"la prima alternativa non funziona con i datetimefields." sarebbe abbastanza sorprendente, dato che datetime.datetime () restituisce un oggetto datetime djangoproject.com/documentation/0.96/models/basic controlla la definizione del modello e gli esempi: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew,

"So che è facoltativo, il problema è che il mio datetimefield ha il tempo impostato, non è 00:00" Oh, ora capisco. Sì, senza argomenti tempo imposta su 00, quindi non ritorna :)
zalew

88

Ecco i risultati che ho ottenuto con la funzione timeit di ipython:

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

contiene sembra essere più veloce.


Questa soluzione sembra essere la più recente. Sono sorpreso che abbia ottenuto 4 voti positivi, perché quando provo la containssoluzione ricevo il messaggio di errore:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman

Ricontrollo e aggiorno i risultati oggi e non credo che il tuo errore sia causato dal __containsfiltro. Ma se stai riscontrando problemi, dovresti provare l' esempio di documenti di django che sta usando __gte.
Moreno,

4
Il metodo __contains funziona bene per me. Penso che questa sia probabilmente la risposta migliore poiché fornisce confronti sulle prestazioni. Ho votato più di uno, ma sono sorpreso che non abbia più voti.
RobotHumans,

Adoro la startswithsoluzione ..
w411, 3

46

Ora Django ha il filtro Queryset __date per interrogare oggetti datetime rispetto alle date nella versione di sviluppo. Pertanto, sarà presto disponibile in 1.9.



Migliore risposta, valida per le nuove versioni di Django
serfer2

Questo non funziona per me, non ho idea del perché :( Sono su Django 1.11 La mia eccezione esatta è: NotImplementedError: le sottoclassi di operazioni basate su database potrebbero richiedere un metodo datetime_cast_date ()
AbdurRehman Khan,

44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

quanto sopra è quello che ho usato. Non solo funziona, ma ha anche un supporto logico intrinseco.


3
Molto meglio di tutte le altre risposte qui, grazie!
Kin

sssss-Shazam! 💯
dps

30

A partire da Django 1.9, il modo per farlo è usando __dateun oggetto datetime.

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


27

Questo produce gli stessi risultati dell'utilizzo di __year, __month e __day e sembra funzionare per me:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))

19
sembra che questo trasformi l'oggetto data in stringa e faccia un confronto di stringhe di date quindi forza db a fare una scansione completa della tabella. per i grandi tavoli questo uccide la tua esibizione
yilmazhuseyin

8

supponendo che active_on sia un oggetto data, incrementalo di 1 giorno, quindi fai intervallo

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

2

Ecco una tecnica interessante: ho sfruttato la procedura startwith implementata con Django su MySQL per ottenere il risultato di cercare un datetime solo attraverso la data. Fondamentalmente, quando Django esegue la ricerca nel database, deve eseguire una conversione di stringa per l'oggetto di archiviazione MySQL DATETIME, in modo da poter filtrare su questo, tralasciando la porzione di data e ora della data, in questo modo% LIKE% corrisponde solo alla data oggetto e otterrai tutti i timestamp per la data specificata.

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

Ciò eseguirà la seguente query:

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

Il LIKE BINARY in questo caso corrisponderà a tutto per la data, indipendentemente dal timestamp. Compresi valori come:

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

Spero che questo aiuti tutti fino a quando Django non esce con una soluzione!


Ok, quindi questa sembra essere la stessa risposta di mhost e kettlehell sopra, ma con più descrizione di ciò che sta accadendo nel backend. Almeno hai un motivo per usare contiene o inizia con l'attributo date () del datetime!
bbengfort

2

C'è un fantastico post sul blog che tratta questo qui: Confronto di date e orari nel Django ORM

La migliore soluzione pubblicata per Django> 1.7, <1.9 è quella di registrare una trasformazione:

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

Quindi puoi usarlo nei tuoi filtri in questo modo:

Foo.objects.filter(created_on__date=date)

MODIFICARE

Questa soluzione dipende sicuramente dal back-end. Dall'articolo:

Naturalmente, questa implementazione si basa sul tuo particolare sapore di SQL con una funzione DATE (). MySQL lo fa. Lo stesso vale per SQLite. D'altra parte, non ho lavorato personalmente con PostgreSQL, ma alcuni googling mi portano a credere che non abbia una funzione DATE (). Quindi un'implementazione così semplice sembra che sarà necessariamente in qualche modo dipendente dal backend.


questo è MySQL specifico? chiedendosi quale sia la scelta del nome della classe.
Binoj David,

@BinojDavid Sì, dipende dal backend
Dan Gayle,

L'ho provato usando Django 1.8 e PostgreSQL 9.6.6 e funziona perfettamente. In realtà usando PostgreSQL puoi anche usare questa sintassi:return '{}::date'.format(lhs), params
michal-michalak

1

Hm .. La mia soluzione funziona:

Mymodel.objects.filter(date_time_field__startswith=datetime.datetime(1986, 7, 28))


0

In Django 1.7.6 funziona:

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

2
Funziona solo se stai cercando la data esatta, non puoi usarla __lteper esempio.
Guiva

-1

Vedi l'articolo Django Documentation

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))

5
nella documentazione di django funziona perché il datetimefiled ha il tempo 00:00
Xidobix,
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.