2009-08-12 6 views
21

Django a la nouvelle fonction annotate() pour les jeux de requête. Cependant, je n'arrive pas à le faire fonctionner correctement pour plusieurs annotations dans un même jeu de requête.Django annotate() plusieurs fois provoque de mauvaises réponses

Par exemple,

tour_list = Tour.objects.all().annotate(Count('tourcomment')).annotate(Count('history')) 

Une visite peut contenir plusieurs entrées de tourcomment et de l'histoire. J'essaie d'obtenir combien de commentaires et d'entrées d'histoire existent pour cette tournée. Les valeurs

history__count and tourcomment__count 

seront incorrectes. S'il n'y a qu'un seul appel annotate(), la valeur sera correcte.

Il semble y avoir une sorte d'effet multiplicatif provenant des deux LEFT OUTER JOIN. Par exemple, si un tour a 3 historiques et 3 commentaires, 9 sera la valeur de comptage pour les deux. 12 histoires + 1 commentaire = 12 pour les deux valeurs. 1 histoire + 0 commentaire = 1 histoire, 0 commentaires (celui-ci arrive à renvoyer les valeurs correctes).

L'appel SQL résultant est:

SELECT `testapp_tour`.`id`, `testapp_tour`.`operator_id`, `testapp_tour`.`name`, `testapp_tour`.`region_id`, `testapp_tour`.`description`, `testapp_tour`.`net_price`, `testapp_tour`.`sales_price`, `testapp_tour`.`enabled`, `testapp_tour`.`num_views`, `testapp_tour`.`create_date`, `testapp_tour`.`modify_date`, `testapp_tour`.`image1`, `testapp_tour`.`image2`, `testapp_tour`.`image3`, `testapp_tour`.`image4`, `testapp_tour`.`notes`, `testapp_tour`.`pickup_time`, `testapp_tour`.`dropoff_time`, COUNT(`testapp_tourcomment`.`id`) AS `tourcomment__count`, COUNT(`testapp_history`.`id`) AS `history__count` 
FROM `testapp_tour` LEFT OUTER JOIN `testapp_tourcomment` ON (`testapp_tour`.`id` = `testapp_tourcomment`.`tour_id`) LEFT OUTER JOIN `testapp_history` ON (`testapp_tour`.`id` = `testapp_history`.`tour_id`) 
GROUP BY `testapp_tour`.`id` 
ORDER BY `testapp_tour`.`name` ASC 

J'ai essayé de combiner les résultats de deux QuerySets qui contiennent un seul appel à annoter(), mais cela ne fonctionne pas bien ... Vous ne pouvez pas vraiment garantir que la commande sera la même. et il semble trop compliqué et salissant donc j'ai cherché quelque chose de mieux ...

tour_list = Tour.objects.all().filter(operator__user__exact = request.user).filter(enabled__exact = True).annotate(Count('tourcomment')) 
tour_list_historycount = Tour.objects.all().filter(enabled__exact = True).annotate(Count('history')) 
for i,o in enumerate(tour_list): 
    o.history__count = tour_list_historycount[i].history__count 

Merci pour toute aide. Stackoverflow a sauvé mes fesses dans le passé avec beaucoup de questions déjà répondues, mais je n'ai pas encore trouvé de réponse à celle-ci.

Répondre

0

Je ne peux pas garantir que cela va résoudre votre problème, mais essayez d'ajouter .order_by() à votre appel. C'est:

tour_list = Tour.objects.all().annotate(Count('tourcomment')).annotate(Count('history')).order_by() 

La raison est que django doit sélectionner tous les champs de la clause ORDER BY, ce qui provoque autrement des résultats identiques à sélectionner. En ajoutant .order_by(), vous supprimez complètement la clause ORDER BY, ce qui empêche que cela se produise. Voir the aggregation documentation pour plus d'informations sur ce problème.

40

Merci pour votre commentaire. Cela n'a pas vraiment fonctionné, mais cela m'a orienté dans la bonne direction. J'ai enfin pu résoudre ce problème en ajoutant distinct à la fois Count() appelle:

Count('tourcomment', distinct=True) 
+1

... qui est encore une solution horrible, car ce filtre simplement tous les doublons de l'énorme résultat – dragonroot

+1

Cela fonctionne qu'avec Compter. J'ai un problème similaire avec un nombre et une somme, et bien que la valeur de distinct à vrai garde le nombre exact, la somme est encore multipliée – StephenTG

+0

Aussi, pour une description de bug Sum, voir: https: //code.djangoproject .com/ticket/10060 – sobolevn

Questions connexes