在Django中的列之间进行聚合

时间:2010-03-07 01:19:39

标签: python django orm aggregate

我试图找出是否有办法在Django中使用其ORM进行有点复杂的聚合,或者如果我将不得不使用extra()来粘贴一些原始SQL。

以下是我的对象模型(剥离以显示要点):

class Submission(Models.model)
  favorite_of = models.ManyToManyField(User, related_name="favorite_submissions")

class Response(Models.model)
  submission = models.ForeignKey(Submission)
  voted_up_by = models.ManyToManyField(User, related_name="voted_up_responses")

我想要做的是对给定提交的所有投票进行总结:即,对其任何回复的所有投票,然后还包括将提交标记为收藏的人数。

我的第一部分使用以下代码;这将返回每次提交的所有回复的总投票数:

submission_list = Response.objects\
  .values('submission')\
  .annotate(votes=Count('voted_up_by'))\
  .filter(votes__gt=0)\
  .order_by('-votes')[:TOP_NUM]

(因此,在获得总投票后,我按降序排序并返回TOP_NUM顶部提交,以获得“最佳”列表。)

那部分有效。您是否有任何方式可以建议在其投票中包含每个提交的人数? (我宁愿避免使用extra()来实现可移植性,但我认为它可能是必要的,而且我愿意使用它。)

编辑:我在阅读下面的建议后意识到我应该更清楚地描述问题。理想的解决方案是允许我按总票数(voted_up_byfavorited的总和)进行排序,然后在数据库中选择前几个。如果那不可能,那么我愿意加载每个响应的一些字段并在Python中进行处理;但由于我将处理100,000多条记录,因此避免这种开销会很好。 (另外,亚当和德米特里:我很抱歉延迟回应!)

2 个答案:

答案 0 :(得分:1)

一种可能性是稍微重新安排您当前的查询。如果你尝试过以下内容怎么办?

submission_list = Response.objects\
    .annotate(votes=Count('voted_up_by'))\
    .filter(votes__gt=0)\
    .order_by('-votes')[:TOP_NUM]
submission_list.query.group_by = ['submission_id']

这将返回Response对象的查询集(具有相同提交的对象将被集中在一起)。要访问相关提交和/或favorite_of列表/计数,您有两种选择:

num_votes = submission_list[0].votes
submission = submission_list[0].submission
num_favorite = submission.favorite_of.count()

...或

submissions = []
for response in submission_list:
    submission = response.submission
    submission.votes = response.votes
    submissions.append(submission)
num_votes = submissions[0].votes
submission = submissions[0]
num_favorite = submission.favorite_of.count()

基本上第一个选项的好处仍然是查询集,但您必须确保访问提交对象以获取有关提交的任何信息(因为查询集中的每个对象在技术上都是响应)。第二个选项的好处是既包含favorite_of列表又包含投票的提交列表,但它不再是一个查询集(因此请确保您之后不再需要更改查询)。

答案 1 :(得分:0)

您可以在其他查​​询中计算收藏夹,例如

favorite_list = Submission.objects.annotate(favorites=Count(favorite_of))

之后,您可以添加两个列表中的值:

total_votes = {}
for item in submission_list:
    total_votes[item.submission.id] = item.voted_by
for item in favorite_list:
    has_votes = total_votes.get(item.id, 0)
    total_votes[item.id] = has_votes + item.favorites

我在字典中使用id,因为Submission对象不一样。如果您自己需要提交,您可以使用一个字典或存储元组(提交,投票)而不仅仅是投票。

补充:此解决方案优于之前的解决方案,因为您只有两个数据库请求。

相关问题