Django annotate()多次导致错误的答案

时间:2009-08-12 09:35:05

标签: sql django django-queryset

Django为查询集提供了很棒的新annotate()函数。但是,我无法让它在单个查询集中正确处理多个注释。

例如,

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

巡视可以包含多个tourcomment和历史记录条目。我想知道这次巡演有多少评论和历史条目。结果

history__count and tourcomment__count

值不正确。如果只有一个annotate()调用,则该值将是正确的。

两个LEFT OUTER JOIN似乎有某种乘法效应。例如,如果一个巡视有3个历史记录和3个评论,那么9个将是两者的计数值。 12个历史记录+ 1个评论= 12个值。 1个历史记录+ 0个评论= 1个历史记录,0条评论(这个评论恰好返回正确的值)。

生成的SQL调用是:

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

我尝试将两个查询集的结果组合在一起,这两个查询集包含对annotate()的单个调用,但它无法正常工作......您无法保证订单将是相同的。它看起来过于复杂和混乱,所以我一直在寻找更好的东西......

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

感谢您的帮助。 Stackoverflow过去已经用很多已经回答的问题保存了我的屁股,但是我还没有找到答案。

3 个答案:

答案 0 :(得分:44)

感谢您的评论。这并没有奏效,但它引导我朝着正确的方向前进。我终于能够通过向Count()调用添加distinct来解决这个问题:

Count('tourcomment', distinct=True)

答案 1 :(得分:0)

我无法保证这会解决您的问题,但请尝试将.order_by()附加到您的通话中。那就是:

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

原因是django需要选择ORDER BY子句中的所有字段,这会导致选择相同的结果。通过附加.order_by(),您将完全删除ORDER BY子句,从而防止这种情况发生。有关此问题的详情,请参阅the aggregation documentation

答案 2 :(得分:0)

tour_list = Tour.objects.all().annotate(tour_count=Count('tourcomment',distinct=True) ).annotate(history_count=Count('history',distinct=True) )

您必须添加distinct=True才能获得正确的结果,否则它将返回错误的答案。