You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django一对多关系中按分组嵌套聚合Avg查询问题

解决方案:为商家实例关联评价问题的平均评分

嘿,我来帮你搞定这个需求!首先得说,我注意到你给出的模型里缺了关键的Review(评价)模型——毕竟要计算平均评分,总得有个模型关联商家、评价问题和具体的rate值对吧?我先默认你有这么个模型(后面会给出示例定义),然后给你几种可行的实现方案:

第一步:补全必要的Review模型

如果还没定义这个模型,先补上:

class Review(models.Model):
    business = models.ForeignKey(Business, on_delete=models.CASCADE, related_name='reviews')
    review_question = models.ForeignKey(ReviewQuestion, on_delete=models.CASCADE, related_name='reviews')
    rate = models.IntegerField()  # 假设是1-5的评分值

方法1:用Django ORM的Prefetch+Annotate(最推荐,性能友好)

这种方法可以一次性预取所有需要的数据,避免N+1查询,而且结构清晰:

from django.db.models import Avg, Prefetch, Q
from django.db.models.expressions import OuterRef
from .models import Business, ReviewQuestion, Review

# 先定义一个查询集:给每个评价问题带上当前商家的平均评分
question_with_avg_query = ReviewQuestion.objects.annotate(
    avg_rate=Avg(
        'reviews__rate',
        filter=Q(reviews__business=OuterRef('pk'))  # 只计算当前商家的评分
    )
)

# 查询商家时,用Prefetch把上面的查询集关联进来
business_list = Business.objects.prefetch_related(
    Prefetch(
        'business_type__reviewquestion_set',  # 关联到商家类型下的所有评价问题
        queryset=question_with_avg_query,
        to_attr='questions_with_avg'  # 自定义属性名,方便后续访问
    )
).all()

# 遍历使用的示例
for business in business_list:
    print(f"商家:{business.name}")  # 假设Business有name字段
    for question in business.business_type.questions_with_avg:
        avg_rate = question.avg_rate or 0  # 处理没有评分的情况
        print(f"  问题:{question.question_text},平均评分:{avg_rate}")

这种方式的好处是,所有数据一次查询完成,而且每个评价问题的平均评分直接绑定在对应的ReviewQuestion实例上,非常好处理。

方法2:用Subquery实现商家级别的评分聚合

如果需要把所有评分聚合结果直接放在商家实例上,可以用Subquery:

from django.db.models import Avg, Subquery, OuterRef
from .models import Business, Review

# 先为每个商家+评价问题的组合计算平均评分
avg_rate_subquery = Review.objects.filter(
    business=OuterRef('pk')
).values('review_question').annotate(avg_rate=Avg('rate')).values('avg_rate')

# 查询商家时,把聚合结果annotate进去
business_list = Business.objects.annotate(
    question_avg_rates=Subquery(avg_rate_subquery)
).select_related('business_type').prefetch_related('business_type__reviewquestion_set')

不过这种方式返回的question_avg_rates是一个列表,需要和商家的评价问题一一对应,处理起来不如第一种直观。

方法3:原生SQL查询(适合复杂场景)

如果ORM的写法满足不了你的复杂需求,可以直接用原生SQL:

business_list = Business.objects.raw('''
    SELECT 
        b.id, b.name,  -- 替换成你的Business模型字段
        rq.id as question_id, rq.question_text,
        COALESCE(AVG(r.rate), 0) as avg_rate
    FROM your_app_business b
    JOIN your_app_businesstype bt ON b.business_type_id = bt.id
    LEFT JOIN your_app_reviewquestion rq ON bt.id = rq.business_type_id
    LEFT JOIN your_app_review r ON b.id = r.business_id AND rq.id = r.review_question_id
    GROUP BY b.id, rq.id
    ORDER BY b.id
''')

# 遍历示例
for item in business_list:
    print(f"商家ID:{item.id},商家名称:{item.name}")
    print(f"  问题ID:{item.question_id},问题内容:{item.question_text},平均评分:{item.avg_rate}")

注意要把your_app替换成你的Django应用名称,字段也要对应你的实际模型字段。

内容的提问来源于stack exchange,提问作者ermir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:27:54