Como posso filtrar uma data de um DateTimeField no Django?

173

Estou tentando filtrar uma DateTimeFieldcomparação com uma data. Quero dizer:

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

Recebo uma lista vazia do conjunto de consultas como resposta porque (acho) não estou pensando em tempo, mas quero "a qualquer momento".

Existe uma maneira fácil no Django para fazer isso?

Eu tenho o tempo no horário definido, não é 00:00.

Xidobix
fonte
5
Este é um dos aborrecimentos do Django. Considerando que este é um caso de uso simples e comum, não há uma maneira simples de conseguir isso.
rubayeet

Respostas:

114

Essas pesquisas são implementadas da django.views.generic.date_basedseguinte maneira:

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

Por ser bem detalhado, existem planos para melhorar a sintaxe usando o __dateoperador. Marque " # 9596 A comparação de um DateTimeField com uma data é muito difícil " para obter mais detalhes.

Piotr Czapla
fonte
4
Usando com range: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
Parece que ele chegará ao Django 1.9: github.com/django/django/commit/…
amjoconn 22/10/2015
14
Novo no Django 1.9:Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Não
102
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// editar após comentários

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

não funciona porque cria um objeto datetime com valores de tempo definidos como 0, portanto, o tempo no banco de dados não corresponde.

zalew
fonte
thx pela resposta! a primeira alternativa não funciona com campos de dados. A segunda alternativa funciona;). Se alguém conhece outro método resposta, por favor
Xidobix
docs.python.org/library/datetime.html#datetime-objects usando datetime () do módulo datetime hrs, mins, segundos é opcional. o segundo é de um projeto trabalhando com vars substituído, você pode olhar nos docs é correto
Zalew
eu sei que é opcional, o problema é que meu DateTimeField tem o tempo setado, não é 00:00
Xidobix
"a primeira alternativa não funciona com campos de dados." seria bastante surpreendente, pois datetime.datetime () retorna um objeto datetime djangoproject.com/documentation/0.96/models/basic, verifique a definição do modelo e exemplos: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew 23/08/09
"eu sei que é opcional, o problema é que meu campo de dados tem o tempo definido, não são 00:00" Oh, agora entendi. Sim, sem argumentos de tempo que define a 00, para que ele não voltar :)
Zalew
88

Aqui estão os resultados que obtive com a função timeit do 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

contém parece ser mais rápido.

Moreno
fonte
Esta solução parece ser a mais recente. Estou surpreso ele ficou 4 upvotes, porque quando eu tento a containssolução, eu recebo a mensagem de erro:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Verifico e atualizo novamente os resultados hoje e não acho que seu erro foi causado pelo __containsfiltro. Mas se você estiver com problemas, tente o exemplo do django docs que está usando __gte.
Moreno
4
O método __contains funciona bem para mim. Acho que essa é provavelmente a melhor resposta, pois fornece comparações de desempenho. Eu votei em mais de um, mas estou surpreso que não tenha mais votos.
precisa saber é o seguinte
Eu amo a startswithsolução ..
w411 28/06/06
46

Agora o Django possui __date queryyset filter para consultar objetos datetime contra datas na versão de desenvolvimento. Assim, estará disponível em 1.9 em breve.

onurmatik
fonte
Melhor resposta, válido para as versões mais recentes do Django
serfer2
Isso não funciona para mim, nenhuma idéia porque :( Eu estou no django 1,11 Meu exceção exata é: NotImplementedError: subclasses de basedatabaseoperations pode exigir um método datetime_cast_date ()
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

o acima é o que eu usei. Não apenas funciona, como também possui algum apoio lógico inerente.

chaleira
fonte
3
Muito melhor do que todas as outras respostas aqui, obrigado!
Kin
sssss-shazam! 💯
dps
30

A partir do Django 1.9, a maneira de fazer isso é usando __dateum objeto datetime.

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

Andrew B.
fonte
27

Isso produz os mesmos resultados do uso de __ano, __ meses e __ dias e parece funcionar para mim:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
fonte
19
parece que este transforma o objeto de data em string e faz uma comparação de string de datas, portanto, força o db a fazer uma varredura completa da tabela. para grandes mesas este matar o seu desempenho
yilmazhuseyin
8

supondo que active_on é um objeto de data, aumente-o em 1 dia e depois alcance

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

Aqui está uma técnica interessante - eu alavanquei o procedimento startwith, conforme implementado com o Django no MySQL, para obter o resultado de procurar apenas um datetime até a data. Basicamente, quando o Django faz a pesquisa no banco de dados, ele precisa fazer uma conversão de string para o objeto de armazenamento DATETIME MySQL, para que você possa filtrar isso, deixando de fora a parte do carimbo de data / hora da data - dessa forma% LIKE% corresponde apenas à data objeto e você receberá todos os registros de data e hora para a data especificada.

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

Isso executará a seguinte consulta:

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

O BINÁRIO COMO neste caso corresponderá a tudo para a data, independentemente do registro de data e hora. Incluindo valores como:

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

Espero que isso ajude todos até que o Django saia com uma solução!

bbengfort
fonte
Ok, então essa parece ser a mesma resposta que mhost e kettlehell acima, mas com mais descrição do que está acontecendo no back-end. Pelo menos você tem um motivo para usar contém ou inicia com o atributo date () da data e hora!
bbengfort
2

Há um post fantástico no blog que aborda isso aqui: Comparando datas e datas no Django ORM

A melhor solução publicada para Django> 1.7, <1.9 é registrar uma transformação:

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

Então você pode usá-lo em seus filtros como este:

Foo.objects.filter(created_on__date=date)

EDITAR

Esta solução é definitivamente dependente do back-end. Do artigo:

Obviamente, essa implementação depende do seu sabor específico do SQL com uma função DATE (). MySQL faz. O mesmo acontece com o SQLite. Por outro lado, eu não trabalhei pessoalmente com o PostgreSQL, mas algumas pesquisas me levam a acreditar que ele não possui uma função DATE (). Portanto, uma implementação tão simples parece ser necessariamente um pouco dependente de back-end.

Dan Gayle
fonte
isso é específico do MySQL? perguntando sobre a escolha do nome da classe.
Binoj David 27/10/2015
@BinojDavid Sim, é backend dependente
Dan Gayle
Eu testei usando o Django 1.8 e o PostgreSQL 9.6.6 e funciona perfeitamente. Na verdade usando PostgreSQL você também pode usar esta sintaxe:return '{}::date'.format(lhs), params
Michal-michalak
1

Hm .. Minha solução está funcionando:

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

No Django 1.7.6 funciona:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
fonte
2
Isso só funciona se você estiver procurando a data exata, não pode usar, __ltepor exemplo.
guival
-1

Veja o artigo Django Documentation

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
fonte
5
na documentação do django, funciona porque o datetimefiled tem tempo 00:00
Xidobix 23/08/09