Django 查询 - 是不是可以在数据库级别按公共字段对元素进行分组?
Posted
技术标签:
【中文标题】Django 查询 - 是不是可以在数据库级别按公共字段对元素进行分组?【英文标题】:Django query - Is it possible to group elements by common field at database level?Django 查询 - 是否可以在数据库级别按公共字段对元素进行分组? 【发布时间】:2014-04-16 02:09:37 【问题描述】:我有一个如下所示的数据库模型。将数据视为 2 本书,每本书有 3 个评分。
class Book(models.Model):
name = models.CharField(max_length=50)
class Review(models.Model):
book = models.ForeignKey(Book)
review = models.CharField(max_length=1000)
rating = models.IntegerField()
问题:是否可以将每本书的所有评级分组到一个列表中,用一个单个查询。我希望在数据库级别执行此操作,而不在我的代码中迭代查询集。输出应该类似于:
'book__name':'book1',
'rating' : [3, 4, 4],
'average' : 3.66,
'book__name':'book2',
'rating : [2, 1, 1] ,
'average' : 1.33
我试过这个查询,但评分既不是按书名分组的,也不是平均正确的:
Review.objects.annotate(average=Avg('rating')).values('book__name','rating','average')
编辑:补充说明我正在寻找一种在数据库级别对元素进行分组的方法。
【问题讨论】:
@KevinBrown :您正在进行微不足道的编辑。在此过程中,您从问题中删除了说明。我把它回滚了,你又做了一次。如果您有任何疑问,请将其放在 cmets 中,而不是通过重复进行相同的编辑来表示不尊重。关于重复标签,当您的 Meta SO 问题有一些 cmets/answers 时,我会删除它。 抱歉,我突然想到在编辑完成后我几乎立即恢复,但到那时(我认为?)为时已晚。我对重新标记的编辑大战不感兴趣,我会在继续之前等待 Meta SO 问题。 【参考方案1】:你可以这样做。希望这会有所帮助。
Review.objects.values('book__name').annonate(average=Avg('rating'))
更新:
如果您想要列表中某本书的所有评分,那么您可以这样做。
from collections import defaultdict
ratings = defaultdict(list)
for result in Review.objects.values('book__name', 'rating').order_by('book__name', 'rating'):
ratings[result['book__name']].append(result['rating'])
你会得到这样的结构:
[ book__name: [rating1, rating2, ] , ]
更新:
q = Review.objects.values('book__name').annonate(average=Avg('rating')).filter().prefetech_related('rating')
q[0].ratings.all() # gives all the ratings of a particular book name
q[0].average # gives average of all the ratings of a particular book name
希望这可行(我不确定,抱歉),但您需要添加 related_ name
属性
class Review(models.Model):
book = models.ForeignKey(Book, related_name='rating')
更新:
抱歉,您需要在 SQL 中称为 GROUP_CONCAT 的东西,但目前 Django ORM 不支持它。
您可以使用原始 SQL 或 itertools
from django.db import connection
sql = """
SELECT name, avg(rating) AS average, GROUP_CONCAT(rating) AS rating
FROM book JOIN review on book.id = review.book_id
GROUP BY name
"""
cursor = connection.cursor()
cursor.execute(sql)
data = cursor.fetchall()
DEMO
【讨论】:
是的,这适用于平均水平。但是,如何将评级作为按书名分组的列表。这甚至可能吗? 这是正确的,但我应该更明确地询问是否可以“在数据库抽象 API 中”而不是在视图中。我已经强调了single query
这个词,但也许我试图避免在我的view
中循环遍历查询集并不明显
这是一个有趣的方法。我会进一步阅读。它不适用于当前形式。进一步的 Django 文档说prefetch_related does a separate lookup for each relationship, and does the ‘joining’ in Python
- 可能做同样的事情我试图避免。我会尝试提到select_related
。感谢您的帮助。
我阅读了 select_related 和 prefetch_related。它们主要用于遵循 ForeignKey '类型' 关系。在这种情况下它不起作用。计算平均值时,数据已经存在。我如何告诉数据库对每个字段进行分组?
嗨——我已经更新了答案。它使用单个查询。 :) 如果可行,请将其标记为已接受 :)以上是关于Django 查询 - 是不是可以在数据库级别按公共字段对元素进行分组?的主要内容,如果未能解决你的问题,请参考以下文章
Zappa Django:它不是重新查询数据库,而是使用过时的内存缓存。这种行为可以改变吗?