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 查询 - 是不是可以在数据库级别按公共字段对元素进行分组?的主要内容,如果未能解决你的问题,请参考以下文章

django怎么判断数据库的记录是不是存在

Zappa Django:它不是重新查询数据库,而是使用过时的内存缓存。这种行为可以改变吗?

在 Entity Framework Core 中,如何使用数据库级别的条件(不是本地 LINQ)查询连接表上的数据?

Oracle 是不是关联嵌套在任何级别的子查询?

Django Q 对象(复杂查询)是不是安全?

Django ORM 中的每个事务隔离级别