¿Cómo filtro los objetos de consulta por rango de fechas en Django?

248

Tengo un campo en un modelo como:

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

Ahora, necesito filtrar los objetos por un rango de fechas.

¿Cómo filtro todos los objetos que tienen una fecha entre 1-Jan-2011y 31-Jan-2011?

usuario469652
fuente

Respuestas:

411

Utilizar

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

O si solo está tratando de filtrar por mes:

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

Editar

Como dijo Bernhard Vallant, si desea un conjunto de consultas que excluya la specified range ends, debe considerar su solución , que utiliza gt / lt (mayor que / menor que).

crodjer
fuente
¿Cuál es el tipo de datos de date1? Tengo un objeto datetime ahora.
user469652
8
@dcordjer: Además, debería decirse que __rangeincluye los bordes (como sql's BETWEEN), si no desea que se incluyan los bordes, debería ir con mi solución gt / lt ...
Bernhard Vallant el
¿Está esto inherentemente ordenado en algún orden? Si es así, ¿qué orden? Gracias.
Richard Dunn
1
@RichardDunn El pedido se basará en el pedido predeterminado de su modelo, o si utiliza order_bymás de lo generado QuerySetpor lo mencionado anteriormente filter. No he usado Django en años.
crodjer
para date__range necesita poner 01 del próximo mes. Aquí hay un enlace a la documentación que explica que se traduce a 00: 00: 00.0000 de las fechas, por lo tanto, el último día de su rango no está incluido. docs.djangoproject.com/en/1.10/ref/models/querysets/#range en este caso uso: date__range = ["% s-% s-1"% (año, mes), "% s-% s- 1 "% (año, int (mes) +1)]
SpiRail
195

Puedes usar django'sfilter con datetime.dateobjetos :

import datetime
samples = Sample.objects.filter(sampledate__gte=datetime.date(2011, 1, 1),
                                sampledate__lte=datetime.date(2011, 1, 31))
Bernhard Vallant
fuente
para obtener todo, incluidos los días 1 y 31, tendremos que usar gte ¿verdad?
Sam Stoelinga
1
Una ventaja de usar este método sobre crodjer es que puede pasarle objetos de fecha y hora en lugar de cadenas.
Brian Kung
79

Al hacer rangos de django con un filtro, asegúrese de saber la diferencia entre usar un objeto de fecha frente a un objeto de fecha y hora. __range incluye fechas, pero si utiliza un objeto datetime para la fecha de finalización, no incluirá las entradas para ese día si no se establece la hora.

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

devuelve todas las entradas desde la fecha de inicio hasta la fecha de finalización, incluidas las entradas en esas fechas. Mal ejemplo, ya que esto está devolviendo entradas una semana en el futuro, pero obtienes la deriva.

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

faltarán 24 horas de entradas dependiendo de la hora para los campos de fecha configurados.

cademan
fuente
55
Creo que es importante tener en cuenta cómo importar un dateobjeto: >>> from datetime import date >>> startdate = date.today()
Alex Spencer
19

Puede evitar la "falta de coincidencia de impedancia" causada por la falta de precisión en la DateTimeField/datecomparación de objetos, que puede ocurrir si se usa el rango , usando datetime.timedelta para agregar un día a la última fecha en el rango. Esto funciona como:

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

Como se discutió anteriormente, sin hacer algo como esto, los registros se ignoran el último día.

Editado para evitar el uso de datetime.combine- parece más lógico seguir con las instancias de fecha cuando se compara con a DateTimeField, en lugar de meterse con datetimeobjetos desechables (y confusos) . Ver más explicaciones en los comentarios a continuación.

trojjer
fuente
1
Hay una impresionante biblioteca de Delorean que simplifica esto con un método de truncamiento: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer
@tojjer: parece prometedor, ¿cómo utilizamos el método truncar aquí?
eugene
@eugene: exploré esto nuevamente ahora, después de todos esos meses, y tienes razón en que realmente no ayuda en esta situación después de todo. La única forma de evitarlo que se me ocurre es como se sugirió en mi respuesta original, que es proporcionar el 'relleno' adicional para la comparación con un campo de modelo de fecha y hora cuando se filtra en una instancia de fecha. Esto se puede hacer a través del método datetime.combine como se indicó anteriormente, pero descubrí que puede ser un poco más simple simplemente acomodar la discrepancia agregando un timedelta (días = 1) a la fecha de inicio / finalización en el rango: - Dependiendo del problema.
trojjer
Por Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)])lo tanto , no incluiría los objetos creados date(2014, 2, 1), como @cademan explicó útilmente. Pero si incrementa la fecha de finalización agregando un día, obtendría un conjunto de consultas que cubra esos objetos faltantes (y convenientemente omita los objetos creados date(2014, 2, 2)debido a la misma peculiaridad). Lo molesto aquí es que un rango 'manual' especificado con created__gte ... created__lte=date(2014, 2, 1)tampoco funciona, lo que definitivamente es contradictorio en mi humilde opinión.
trojjer
1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] funciona para mí
eugene
1

Es simple,

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

Funciona para mi

Jonhatan Fajardo
fuente
3
Esto también funcionó para mí, para los novatos por claridad: (date__date = ...) significa ({whateverColumnTheDateIsCalled} __ date)
Ryan Dines
2
OP pidió un rango sin embargo
aliasav
1

Para hacerlo más flexible, puede diseñar un FilterBackend de la siguiente manera:

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
fuente
-2

Sigue siendo relevante hoy. También puedes hacer:

import dateutil
import pytz

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