Django查询 - 是否可以在数据库级别按公共字段对元素进行分组?

use*_*ser 1 django django-models django-queryset django-aggregation

我有一个数据库模型,如下所示.将数据视为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()
Run Code Online (Sandbox Code Playgroud)

问题:是否可以使用单个查询对列表中的所有评级进行分组.我希望在数据库级别执行此操作,而无需在代码中迭代Queryset.输出应该类似于:

{
 'book__name':'book1', 
 'rating'    : [3, 4, 4], 
 'average'   : 3.66,
 'book__name':'book2', 
 'rating     : [2, 1, 1] ,
 'average'   : 1.33
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试过这个查询,但是按照书名分类的评分也不是,平均值也不正确:

Review.objects.annotate(average=Avg('rating')).values('book__name','rating','average')
Run Code Online (Sandbox Code Playgroud)

编辑:添加说明我正在寻找一种方法来在数据库级别对元素进行分组.

Ani*_*hah 8

你可以这样做.希望这可以帮助.

Review.objects.values('book__name').annonate(average=Avg('rating'))
Run Code Online (Sandbox Code Playgroud)

更新:

如果您想要列表中特定图书的所有评级,那么您可以执行此操作.

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'])
Run Code Online (Sandbox Code Playgroud)

你会得到这样的结构:

[{ book__name: [rating1, rating2, ] }, ]
Run Code Online (Sandbox Code Playgroud)

更新:

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
Run Code Online (Sandbox Code Playgroud)

希望这有效(我不确定,对不起),但你需要添加related_ name属性

class Review(models.Model):
     book = models.ForeignKey(Book, related_name='rating')
Run Code Online (Sandbox Code Playgroud)

更新:

很抱歉,但是你需要在SQL中称为GROUP_CONCAT的东西,但目前在Django ORM中不支持它.

您可以使用Raw 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()
Run Code Online (Sandbox Code Playgroud)

DEMO