2009-09-03 5 views
1

J'ai un tas d'objets qui ont une valeur et un champ de date:requête pour les valeurs en fonction de la date w/Django ORM

obj1 = Obj(date='2009-8-20', value=10) 
obj2 = Obj(date='2009-8-21', value=15) 
obj3 = Obj(date='2009-8-23', value=8) 

Je veux que ce retour:

[10, 15, 0, 8] 

ou mieux encore , un agrégat du total jusqu'à ce point:

[10, 25, 25, 33] 

je serais mieux pour obtenir ces données directement à partir de la base de données, mais sinon je ne peux faites le total assez facilement avec un forloop.

J'utilise ORM de Django et aussi Postgres

modifier:

Juste à noter que mon exemple ne couvre que quelques jours, mais en pratique, j'avoir des centaines d'objets couvrant quelques décennies. .. Ce que je suis en train de faire est de créer un graphique linéaire montrant comment la somme de tous mes objets a augmenté au fil du temps (un temps très long)

+0

Base de données? Quelle base de données? Jusqu'à ce que nous arrivions à la fin et voyons dans les balises que vous utilisez Django (et donc probablement son ORM intégré - si vous en utilisez un autre, vous devriez avoir spécifié!), Cette question est très confuse; à l'avenir, s'il vous plaît mettre cette information dans le sujet ou autrement à l'avant. J'ai fait l'édition appropriée ici. –

+0

woops, désolé. Ouais j'utilise django ORM et j'utilise aussi Postgres – priestc

+0

Peut-on supposer qu'il n'y a qu'un ou zéro objet par date? – Wogan

Répondre

4

Celui-ci n'a pas été testé, car il est un peu trop d'une douleur à mettre en place une table Django pour tester avec:

from datetime import date, timedelta 
# http://www.ianlewis.org/en/python-date-range-iterator 
def datetimeRange(from_date, to_date=None): 
    while to_date is None or from_date <= to_date: 
     yield from_date 
     from_date = from_date + timedelta(days = 1) 

start = date(2009, 8, 20) 
end = date(2009, 8, 23) 
objects = Obj.objects.filter(date__gte=start) 
objects = objects.filter(date__lte=end) 

results = {} 
for o in objects: 
    results[o.date] = o.value 

return [results.get(day, 0) for day in datetimeRange(start, end)] 

Cela évite l'exécution d'une requête distincte pour chaque jour.

+0

Les querys de Django sont évalués paresseusement, donc vous finissez par exécuter une requête séparée pour chaque jour. – David

+1

Sauf que le queryset ici n'est évalué qu'une seule fois, au début de la boucle for, donc c'est bien. – Wogan

+0

Chaque fois que o.value est appelée, vous accédez à la base de données, ce qui correspond à la base de données de chaque jour. – David

0
result_list = [] 
for day in range(20,24):  
    result = Obj.objects.get(date=datetime(2009, 08, day)) 
    if result: 
     result_list.append(result.value) 
    else: 
     result_list.append(0) 
return result_list 

Si vous avez plus d'un Obj par jour, vous Il faudra vérifier len (obj) et passer en revue au cas où il y en aurait plus de 1.

0

Si vous parcourez 100 fois Obj.objects.get, vous effectuez 100 requêtes SQL. Obj.objects.filter renvoie les résultats dans une requête SQL, mais vous sélectionnez également tous les champs de modèle. La bonne façon d'y parvenir est d'utiliser Obj.objects.values_list, qui le fera avec une seule requête, et de sélectionner uniquement le champ 'values'.

start_date = date(2009, 8, 20) 
end_date = date(2009, 8, 23) 

objects = Obj.objects.filter(date__range=(start_date,end_date)) 
# values_list and 'value' aren't related. 'value' should be whatever field you're querying 
val_list = objects.values_list('value',flat=True) 
# val_list = [10, 15, 8] 

Pour faire un agrégat en cours d'exécution de val_list, vous pouvez le faire (pas certain que ce soit la façon la plus pythonique)

for i in xrange(len(val_list)): 
    if i > 0: 
     val_list[i] = val_list[i] + val_list[i-1] 

# val_list = [10,25,33] 

EDIT: Si vous devez tenir compte des jours manquants, @Glenn La réponse de Maynard est plutôt bonne, bien que je préfère la syntaxe dict():

objects = Obj.objects.filter(date__range=(start_date,end_date)).values('date','value') 
val_dict = dict((obj['date'],obj['value']) for obj in objects) 
# I'm stealing datetimeRange from @Glenn Maynard 
val_list = [val_dict.get(day, 0) for day in datetimeRange(start_date, end_date)] 
# val_list = [10,15,0,8] 
+0

Cette réponse ne donne pas de valeur pour les jours qui n'existent pas dans la base de données. – Wogan

+1

-1 pour faire de fausses affirmations sur ma solution, puis donner exactement la même solution que moi (mais de manière moins claire). –

+0

Lorsque j'ai écrit ceci, il n'était pas clair pour moi que la DB aurait des dates manquantes. Vous avez raison, si le DB a des dates manquantes alors votre solution est correcte. – David

Questions connexes