我试图找出是否有办法在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_by
和favorited
的总和)进行排序,然后在数据库中选择前几个。如果那不可能,那么我愿意加载每个响应的一些字段并在Python中进行处理;但由于我将处理100,000多条记录,因此避免这种开销会很好。 (另外,亚当和德米特里:我很抱歉延迟回应!)
答案 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对象不一样。如果您自己需要提交,您可以使用一个字典或存储元组(提交,投票)而不仅仅是投票。
补充:此解决方案优于之前的解决方案,因为您只有两个数据库请求。