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.


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.


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.


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.


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.


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.


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


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

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

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!


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.


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


0

Dans Django 1.7.6 fonctionne:

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

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

5
dans la documentation de django, cela fonctionne car le datetimefiled a l'heure 00:00
Xidobix
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.