Comment filtrer une date d'un DateTimeField dans Django?

173

J'essaye de filtrer une DateTimeFieldcomparaison avec une date. Je veux dire:

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

J'obtiens une liste de requêtes vide comme réponse parce que (je pense) je ne considère pas le temps, mais je veux "à tout moment".

Y a-t-il un moyen facile dans Django de faire cela?

J'ai le temps dans le datetime réglé, ce n'est pas le cas 00:00.

Xidobix
la source
5
C'est l'un des désagréments de Django. Étant donné qu'il s'agit d'un cas d'utilisation simple et courant, il n'y a pas de moyen simple d'y parvenir.
rubayeet

Réponses:

114

Ces recherches sont implémentées django.views.generic.date_basedcomme suit:

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

Comme il est assez détaillé, il est prévu d'améliorer la syntaxe à l'aide de l' __dateopérateur. Cochez " # 9596 La comparaison d'un DateTimeField à une date est trop difficile " pour plus de détails.

Piotr Czapla
la source
4
Utilisation avec gamme: Q(created__gte=datetime.combine(created_value, time.min))
Dingo
10
On dirait qu'il atterrira dans Django 1.9: github.com/django/django/commit/…
amjoconn
14
Nouveau dans 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')

// éditer après les commentaires

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

ne fonctionne pas car il crée un objet datetime avec des valeurs d'heure définies sur 0, de sorte que l'heure dans la base de données ne correspond pas.

Zalew
la source
merci pour la réponse! la première alternative ne fonctionne pas avec les champs datetime. La deuxième alternative fonctionne;). Si quelqu'un connaît une autre méthode, veuillez répondre
Xidobix
docs.python.org/library/datetime.html#datetime-objects en utilisant datetime () du module datetime hrs, mins, secs est facultatif. le second est d'un projet de travail avec vars remplacé, vous pouvez regarder dans la documentation c'est correct
zalew
je sais que c'est facultatif, le problème est que mon champ datetime a l'heure réglée, il n'est pas 00:00
Xidobix
"la première alternative ne fonctionne pas avec les champs datetime." ce serait assez surprenant, car datetime.datetime () renvoie un objet datetime djangoproject.com/documentation/0.96/models/basic vérifiez la définition du modèle et les exemples: pub_date = models.DateTimeField () pub_date = datetime (2005, 7, 30)
zalew
"Je sais que c'est facultatif, le problème est que mon champ datetime a l'heure réglée, il n'est pas 00:00" Oh, maintenant je comprends. Oui, sans argument de temps, il est
défini
88

Voici les résultats que j'ai obtenus avec la fonction timeit d'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

contient semble être plus rapide.

Moreno
la source
Cette solution semble être la plus récente. Je suis surpris qu'il ait obtenu 4 votes positifs, car lorsque j'essaye la containssolution, j'obtiens le message d'erreur:Unable to get repr for <class 'django.db.models.query.QuerySet'>
Houman
Je revérifie et mets à jour les résultats aujourd'hui et je ne pense pas que votre erreur soit causée par le __containsfiltre. Mais si vous rencontrez des problèmes, vous devriez essayer l' exemple django docs qui utilise __gte.
Moreno
4
La méthode __contains fonctionne très bien pour moi. Je pense que c'est probablement la meilleure réponse car elle fournit des comparaisons de performances. J'ai voté plus d'un, mais je suis surpris qu'il n'y ait pas plus de votes positifs.
RobotHumans
J'adore la startswithsolution ..
w411 3
46

Django dispose désormais d' un filtre __date queryset pour interroger les objets datetime par rapport aux dates dans la version de développement. Ainsi, il sera bientôt disponible en 1.9.

Onurmatik
la source
Oui, il a été ajouté dans la version 1.9 docs.djangoproject.com/en/1.9/ref/models/querysets/#date
guival le
Meilleure réponse, valable pour les nouvelles versions de Django
serfer2
Cela ne fonctionne pas pour moi, je ne sais pas pourquoi :( Je suis sur django 1.11 Mon exception exacte est: NotImplementedError: les sous-classes d'opérations basedatabase peuvent nécessiter une méthode datetime_cast_date ()
AbdurRehman Khan
44
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

ce qui précède est ce que j'ai utilisé. Non seulement cela fonctionne, mais il a également un support logique inhérent.

kettlehell
la source
3
Bien mieux que toutes les autres réponses ici, merci!
Kin
sssss-shazam! 💯
dps
30

Depuis Django 1.9, la façon de faire est d'utiliser __datesur un objet datetime.

Par exemple: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))

Andrew B.
la source
27

Cela produit les mêmes résultats que l'utilisation de __year, __month et __day et semble fonctionner pour moi:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
la source
19
ressemble à celui-ci transforme l'objet date en chaîne et effectue une comparaison de chaînes de dates oblige donc db à effectuer une analyse complète de la table. pour les grandes tables, celui-ci tue votre performance
yilmazhuseyin
8

en supposant que active_on est un objet date, incrémentez-le de 1 jour puis faites range

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

Voici une technique intéressante - j'ai exploité la procédure startswith telle qu'implémentée avec Django sur MySQL pour obtenir le résultat de rechercher uniquement une date / heure à travers uniquement la date. Fondamentalement, lorsque Django effectue la recherche dans la base de données, il doit effectuer une conversion de chaîne pour l'objet de stockage DATETIME MySQL, vous pouvez donc filtrer sur cela, en laissant de côté la partie horodatage de la date - de cette façon% LIKE% ne correspond qu'à la date objet et vous obtiendrez chaque horodatage pour la date donnée.

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

Cela exécutera la requête suivante:

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

Le LIKE BINARY dans ce cas correspondra à tout pour la date, quel que soit l'horodatage. Y compris des valeurs telles que:

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

J'espère que cela aidera tout le monde jusqu'à ce que Django propose une solution!

bbengfort
la source
Ok, donc cela semble être la même réponse que mhost et kettlehell ci-dessus, mais avec plus de description de ce qui se passe dans le backend. Au moins vous avez une raison d'utiliser contient ou commence avec l'attribut date () du datetime!
bbengfort
2

Il y a un article de blog fantastique qui couvre cela ici: Comparaison des dates et des heures de données dans l'ORM Django

La meilleure solution publiée pour Django> 1.7, <1.9 est d'enregistrer une transformation:

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

Ensuite, vous pouvez l'utiliser dans vos filtres comme ceci:

Foo.objects.filter(created_on__date=date)

ÉDITER

Cette solution dépend définitivement du back-end. De l'article:

Bien sûr, cette implémentation repose sur votre saveur particulière de SQL ayant une fonction DATE (). MySQL le fait. Il en va de même pour SQLite. D'un autre côté, je n'ai pas travaillé personnellement avec PostgreSQL, mais certaines recherches sur Google me portent à croire qu'il n'a pas de fonction DATE (). Donc, une implémentation aussi simple semble être nécessairement quelque peu dépendante du backend.

Dan Gayle
la source
est-ce spécifique à MySQL? s'interroger sur le choix du nom de la classe.
Binoj David
@BinojDavid Ouais, il dépend du backend
Dan Gayle
Je l'ai testé avec Django 1.8 et PostgreSQL 9.6.6 et cela fonctionne parfaitement. En fait, en utilisant PostgreSQL, vous pouvez également utiliser cette syntaxe:return '{}::date'.format(lhs), params
michal-michalak
1

Hm .. Ma solution fonctionne:

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

Dans Django 1.7.6 fonctionne:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
la source
2
Cela ne fonctionne que si vous recherchez la date exacte, vous ne pouvez pas utiliser __ltepar exemple.
guival le
-1

Voir l'article Documentation Django

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
la source
5
dans la documentation de django, cela fonctionne car le datetimefiled a l'heure 00:00
Xidobix