305 votes

Comment filtrer les objets d'une requête par plage de dates dans Django ?

J'ai un champ dans un modèle comme :

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

Maintenant, j'ai besoin de filtrer les objets par une plage de dates.

Comment puis-je filtrer tous les objets dont la date se situe entre 1-Jan-2011 y 31-Jan-2011 ?

502voto

crodjer Points 4099

Utilice

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

Ou si vous essayez simplement de filtrer par mois :

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

Modifier

Comme l'a dit Bernhard Vallant, si vous voulez un queryset qui exclut les specified range ends vous devriez envisager sa solution qui utilise la méthode gt/lt (greater-than/less-than).

0 votes

Quel est le type de données de date1 ? J'ai l'objet datetime maintenant.

10 votes

@dcordjer : De plus, il faut dire que __range inclut les bordures (comme la méthode sql BETWEEN ), si vous ne voulez pas que les bordures soient incluses, vous devrez opter pour ma solution gt/lt...

0 votes

Est-ce que cela est intrinsèquement trié dans n'importe quel ordre ? Si oui, dans quel ordre ? Merci.

248voto

Bernhard Vallant Points 18035

Vous pouvez utiliser de django filter con datetime.date objets :

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

102voto

cademan Points 341

Lorsque vous faites des plages django avec un filtre, assurez-vous de connaître la différence entre l'utilisation d'un objet date et d'un objet datetime. __range est inclusif sur les dates mais si vous utilisez un objet datetime pour la date de fin, il n'inclura pas les entrées de ce jour si l'heure n'est pas définie.

from datetime import date, timedelta

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

renvoie toutes les entrées de la date de début à la date de fin, y compris les entrées à ces dates. Ce n'est pas un bon exemple, car il s'agit de renvoyer les entrées une semaine plus tard, mais vous voyez ce que je veux dire.

from datetime import datetime, timedelta

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

manquera 24 heures d'entrées en fonction de l'heure définie pour les champs de date.

9 votes

Je pense qu'il est important de noter comment importer une date objet : >>> from datetime import date >>> startdate = date.today()

26voto

trojjer Points 171

Vous pouvez contourner la "discordance d'impédance" causée par le manque de précision de l'indicateur DateTimeField/date comparaison d'objets - qui peut se produire si l'on utilise gamme -- en utilisant un datetime.timedelta pour ajouter un jour à la dernière date de la plage. Cela fonctionne comme suit :

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

Comme nous l'avons vu précédemment, sans une telle mesure, les enregistrements sont ignorés le dernier jour.

Modifié pour éviter l'utilisation de datetime.combine -- il semble plus logique de s'en tenir aux instances de date lors de la comparaison avec une DateTimeField au lieu de s'embrouiller avec des produits jetables (et confus). datetime objets. Voir les explications supplémentaires dans les commentaires ci-dessous.

1 votes

Il y a une bibliothèque Delorean géniale qui simplifie cela avec une méthode de troncature : delorean.readthedocs.org/fr/latest/quickstart.html#truncation

0 votes

@tojjer : ça semble prometteur, mais comment utiliser la méthode truncate ici ?

0 votes

@eugene : J'ai exploré cela à nouveau juste maintenant, après tous ces mois, et vous avez raison en ce sens que cela n'aide pas vraiment dans cette situation après tout. Le seul moyen de contourner le problème auquel je pense est celui suggéré dans ma réponse initiale, qui consiste à fournir le "rembourrage" supplémentaire pour la comparaison avec un champ de modèle datetime lorsque vous filtrez sur une instance de date. Cela peut être fait via la méthode datetime.combine comme ci-dessus, mais j'ai constaté qu'il peut être un peu plus simple de simplement tenir compte de l'écart en ajoutant un timedelta(days=1) à la date de début/fin de la plage -- selon le problème.

2voto

saran3h Points 1541

Pour le rendre plus flexible, vous pouvez concevoir un FilterBackend comme ci-dessous :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X