¿Cómo puedo filtrar una fecha de DateTimeField en Django?

173

Estoy tratando de filtrar una DateTimeFieldcomparación con una fecha. Quiero decir:

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

Obtengo una lista de consulta vacía como respuesta porque (creo) que no estoy considerando el tiempo, pero quiero "en cualquier momento".

¿Hay una manera fácil en Django para hacer esto?

Tengo la hora en la fecha y hora establecida, no lo es 00:00.

Xidobix
fuente
55
Esta es una de las molestias de Django. Teniendo en cuenta que este es un caso de uso simple y común, no hay una manera simple de lograrlo.
rubayeet

Respuestas:

114

Dichas búsquedas se implementan de la django.views.generic.date_basedsiguiente manera:

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

Debido a que es bastante detallado, hay planes para mejorar la sintaxis utilizando el __dateoperador. Marque " # 9596 Comparar un DateTimeField con una fecha es demasiado difícil " para obtener más detalles.

Piotr Czapla
fuente
44
Uso con rango: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
Parece que aterrizará en Django 1.9: github.com/django/django/commit/…
amjoconn
14
Nuevo en Django 1.9:Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
No
102
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// editar después de comentarios

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

no funciona porque crea un objeto datetime con valores de tiempo establecidos en 0, por lo que el tiempo en la base de datos no coincide

zalew
fuente
Gracias por la respuesta! La primera alternativa no funciona con datetimefields. La segunda alternativa funciona;). Si alguien conoce otro método, responda
Xidobix
docs.python.org/library/datetime.html#datetime-objects usando datetime () del módulo datetime hrs, mins, secs es opcional. el segundo es de un proyecto de trabajo con vars reemplazados, puede ver en los documentos que es correcto
zalew
Sé que es opcional, el problema es que mi campo de fecha y hora tiene la hora establecida, no son las 00:00
Xidobix
"La primera alternativa no funciona con los campos de fecha y hora". sería bastante sorprendente, ya que datetime.datetime () devuelve un objeto datetime djangoproject.com/documentation/0.96/models/basic verifique la definición del modelo y ejemplos: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew
"Sé que es opcional, el problema es que mi campo de fecha y hora tiene la hora establecida, no son las 00:00" Oh, ahora lo entiendo. Sí, sin argumentos de tiempo, se establece en 00, por lo que no vuelve :)
zalew
88

Estos son los resultados que obtuve con la función timeit de 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 parece ser más rápido.

Moreno
fuente
Esta solución parece ser la más reciente. Me sorprende que haya 4 votos a favor, porque cuando pruebo la containssolución, aparece el mensaje de error:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Vuelvo a verificar y actualizo los resultados hoy y no creo que su error sea causado por el __containsfiltro. Pero si se encuentra con problemas, debe probar el ejemplo de django docs que está utilizando __gte.
Moreno
44
El método __contains funciona bien para mí. Creo que esta es probablemente la mejor respuesta, ya que proporciona comparaciones de rendimiento. He votado más de uno, pero me sorprende que no tenga más votos a favor.
RobotHumans
Me encanta la startswithsolución ..
w411 3
46

Ahora Django tiene un filtro de conjunto de consultas __date para consultar objetos datetime contra fechas en la versión de desarrollo. Por lo tanto, estará disponible en 1.9 pronto.

onurmatik
fuente
La mejor respuesta, válida para las nuevas versiones de Django
serfer2
Esto no funciona para mí, no tengo idea de por qué :( Estoy en django 1.11 Mi excepción exacta es: NotImplementedError: las subclases de operaciones basedatabase pueden requerir un método datetime_cast_date ()
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

Lo anterior es lo que he usado. No solo funciona, sino que también tiene un respaldo lógico inherente.

caldera
fuente
3
Mucho mejor que todas las otras respuestas aquí, ¡gracias!
Kin
sssss-shazam! 💯
dps
30

A partir de Django 1.9, la forma de hacerlo es utilizando __dateun objeto datetime.

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

Andrew B.
fuente
27

Esto produce los mismos resultados que usar __year, __month y __day y parece funcionar para mí:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
fantasma
fuente
19
parece que este convierte el objeto de fecha en una cadena y hace una comparación de fechas de la cadena, por lo tanto, obliga a db a hacer un escaneo completo de la tabla. para mesas grandes este mata tu rendimiento
yilmazhuseyin
8

suponiendo que active_on es un objeto de fecha, increméntelo en 1 día y luego haga un rango

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

Aquí hay una técnica interesante: aproveché el procedimiento de inicio con implementado con Django en MySQL para lograr el resultado de buscar solo una fecha y hora solo a través de la fecha. Básicamente, cuando Django realiza la búsqueda en la base de datos, tiene que hacer una conversión de cadena para el objeto de almacenamiento DATETIME MySQL, para que pueda filtrar eso, omitiendo la parte de fecha y hora de la fecha, de esa manera% LIKE% coincide solo con la fecha objeto y obtendrá cada marca de tiempo para la fecha dada.

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

Esto realizará la siguiente consulta:

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

El BINARIO ME GUSTA en este caso coincidirá con todo para la fecha, sin importar la marca de tiempo. Incluyendo valores como:

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

¡Espero que esto ayude a todos hasta que Django salga con una solución!

bbengfort
fuente
Ok, entonces parece ser la misma respuesta que mhost y kettlehell arriba, pero con más descripción de lo que está sucediendo en el backend. ¡Al menos tiene una razón para usar contiene o comienza junto con el atributo date () de datetime!
bbengfort
2

Hay una publicación de blog fantástica que cubre esto aquí: Comparación de fechas y horas en el ORM de Django

La mejor solución publicada para Django> 1.7, <1.9 es registrar una transformación:

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

Luego puede usarlo en sus filtros de esta manera:

Foo.objects.filter(created_on__date=date)

EDITAR

Esta solución definitivamente depende del back end. Del artículo:

Por supuesto, esta implementación se basa en su sabor particular de SQL que tiene una función DATE (). MySQL lo hace. También lo hace SQLite. Por otro lado, no he trabajado con PostgreSQL personalmente, pero algunas búsquedas en Google me hacen creer que no tiene una función DATE (). Por lo tanto, una implementación así de simple parece ser necesariamente dependiente del backend.

Dan Gayle
fuente
¿Esto es específico de MySQL? preguntándose sobre la elección del nombre de la clase.
Binoj David
@BinojDavid Sí, depende del backend
Dan Gayle
Lo probé usando Django 1.8 y PostgreSQL 9.6.6 y funciona perfectamente. En realidad, usando PostgreSQL también puede usar esta sintaxis:return '{}::date'.format(lhs), params
michal-michalak
1

Hm .. Mi solución está funcionando:

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

En Django 1.7.6 funciona:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
fuente
2
Esto solo funciona si está buscando la fecha exacta, no puede usar, __ltepor ejemplo.
Guival
-1

Ver el artículo Documentación de Django

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
fuente
55
en la documentación de django funciona porque la fecha y hora tiene hora 00:00
Xidobix