Como filtrar objetos de consulta por período no Django?


248

Eu tenho um campo em um modelo como:

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

Agora, preciso filtrar os objetos por um período.

Como filtrar todos os objetos que possuem uma data entre 1-Jan-2011e 31-Jan-2011?

Respostas:


409

Usar

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

Ou se você está apenas tentando filtrar os meses:

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

Editar

Como Bernhard Vallant disse, se você quiser um conjunto de consultas que exclua a opção specified range ends, considere a solução dele , que utiliza gt / lt (maior que / menor que).


O que é o tipo de dados date1? Eu tenho o objeto datetime agora.
user469652

8
@dcordjer: Additinally deve ser dito que __rangeinclui as fronteiras (como sql de BETWEEN), se você não quer que as fronteiras incluídos você teria que ir com o meu gt / solução lt ...
Bernhard Vallant

Isso é inerentemente classificado em qualquer ordem? Em caso afirmativo, qual ordem? Obrigado.
Richard Dunn

1
@RichardDunn A ordem será baseada na ordem padrão do seu modelo ou se você usar order_bysobre o gerado QuerySetpelo mencionado acima filter. Eu não uso o Django há anos.
11136 crodjer #

para date__range, você precisa colocar 01 do próximo mês. Aqui está um link para a documentação que mostra que ela se traduz em 00: 00: 00.0000 das datas, portanto, o último dia do seu intervalo não está incluído. docs.djangoproject.com/en/1.10/ref/models/querysets/#range neste caso eu uso: date__range = ["% s-% s-1"% (ano, mês), "% s-% s- 1 "% (ano, int (mês) +1)]
SpiRail 01/09/16

195

Você pode usar django'sfilter com datetime.dateobjetos :

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

para obter tudo, incluindo os dias 1 e 31, teremos que usar gte, certo?
Sam Stoelinga

1
Uma vantagem de usar esse método sobre o crodjer's é que você pode passar objetos de data e hora em vez de strings.
21715 Brian Kung

79

Ao fazer intervalos de django com um filtro, verifique se você sabe a diferença entre usar um objeto de data e um objeto de data e hora. __range é inclusivo nas datas, mas se você usar um objeto datetime para a data final, ele não incluirá as entradas para esse dia se o horário não estiver definido.

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

retorna todas as entradas da data inicial até a data final, incluindo as entradas nessas datas. Exemplo ruim, pois isso está retornando entradas de uma semana para o futuro, mas você entende.

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

faltam 24 horas no valor de entradas, dependendo da hora definida para os campos de data.


5
Eu acho que é importante notar como importar um dateobjeto: >>> from datetime import date >>> startdate = date.today()
Alex Spencer

19

Você pode contornar a "incompatibilidade de impedância" causada pela falta de precisão na DateTimeField/datecomparação de objetos - que pode ocorrer se o intervalo for usado - usando um datetime.timedelta para adicionar um dia à última data no intervalo. Isso 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 discutido anteriormente, sem fazer algo assim, os registros são ignorados no último dia.

Editado para evitar o uso de datetime.combine- parece mais lógico ficar com instâncias de data ao comparar com a DateTimeField, em vez de mexer com datetimeobjetos descartáveis ​​(e confusos) . Veja mais explicações nos comentários abaixo.


1
Há uma biblioteca Delorean impressionante que simplifica isso com um método de truncamento: delorean.readthedocs.org/en/latest/quickstart.html#truncation
trojjer

@ tojjer: parece promissor, como usamos o método truncado aqui?
Eugene

@ Eugene: Eu explorei isso de novo agora, depois de todos esses meses, e você está certo de que isso realmente não ajuda nessa situação, afinal. A única maneira de contornar isso é como sugerido na minha resposta original, que é fornecer o 'preenchimento' extra para comparação com um campo de modelo de data e hora quando você está filtrando uma instância de data. Isso pode ser feito pelo método datetime.combine, como acima, mas descobri que pode ser um pouco mais simples acomodar apenas a discrepância adicionando um timedelta (days = 1) à data de início / término no intervalo - - dependendo do problema.
trojjer

Portanto Example.objects.filter(created__range=[date(2014, 1, 1), date(2014, 2, 1)]), não incluiria objetos criados date(2014, 2, 1), como o @cademan explicou útil. Mas se você incrementasse a data de término adicionando um dia, obteria um conjunto de consultas cobrindo os objetos ausentes (e omitindo convenientemente objetos criados por date(2014, 2, 2)causa da mesma peculiaridade). A coisa chata aqui é que um intervalo 'manual' especificado com created__gte ... created__lte=date(2014, 2, 1)também não funciona, o que é definitivamente IMHO contra-intuitivo.
trojjer

1
@tojjer: datetime_field__range = [delorean.parse ('2014-01-01'). date, delorean.parse ('2014-02-01'). date] funciona para mim
eugene

1

É simples,

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

Funciona para mim


3
Isso funcionou para mim, bem como, para os noobs para maior clareza: (date__date = ...) meios ({} whateverColumnTheDateIsCalled __ data)
Ryan Dines

2
OP pediu uma gama no entanto
aliasav

1

Para torná-lo mais flexível, você pode criar um FilterBackend como abaixo:

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

Ainda relevante hoje. Você também pode fazer:

import dateutil
import pytz

date = dateutil.parser.parse('02/11/2019').replace(tzinfo=pytz.UTC)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.