Django模型:对非直接关联模型执行聚合操作
针对非直接关联模型的聚合操作方案
嘿,针对你给出的UserProfile和UserRating这两个非直接关联的模型,我整理了几个基于Django ORM的聚合实现方案,完全不用写原生SQL就能搞定需求~
核心思路
这两个模型虽然没有直接外键关联,但都和User模型绑定:UserProfile是一对一关联,UserRating是双向外键关联。所以我们可以把User作为中间桥梁,通过ORM的关联查询语法把它们串起来,再结合聚合函数完成统计。
场景1:获取每个用户的Profile信息 + 他们的平均被评分
比如你想统计每个用户的个人资料,同时算出他们收到的所有评分的平均值,可以这么写:
from django.db.models import Avg from django.db.models.functions import Coalesce # 关联User模型,同时聚合平均评分,用Coalesce处理无评分的情况(返回0而非None) profiles_with_avg_rating = UserProfile.objects.select_related('user').annotate( avg_rating=Coalesce(Avg('user__rated_user__rating'), 0) ) # 遍历结果示例 for profile in profiles_with_avg_rating: print(f"用户名: {profile.user.username}") print(f"个人字段: {profile.some_field}") print(f"平均被评分: {profile.avg_rating}\n")
这里的关键是user__rated_user__rating路径:
user是UserProfile关联的User实例rated_user是UserRating中subject_user的related_name,指向所有给这个用户的评分记录- 最后对
rating字段取平均值,用Coalesce避免无评分时返回None
场景2:统计每个评分者的Profile + 他们给出的评分总数/平均分
如果你想从评分者的角度统计,比如看每个用户(带Profile)一共给别人打了多少次分,或者他们给出的评分的平均值:
from django.db.models import Count, Avg # 方案1:从UserProfile出发聚合 raters_profile_stats = UserProfile.objects.select_related('user').annotate( total_given_ratings=Count('user__rating_user__id'), avg_given_rating=Avg('user__rating_user__rating') ) # 方案2:从UserRating出发聚合,再关联Profile rating_stats = UserRating.objects.values( 'rating_user__id', 'rating_user__userprofile__some_field' ).annotate( total_ratings=Count('id'), avg_rating=Avg('rating') ).order_by('-total_ratings')
解释下路径:
user__rating_user是User模型通过UserRating中rating_user的related_name,指向这个用户给出的所有评分记录- 用
values指定要分组的字段(比如评分者ID和他们的Profile字段),再结合annotate做聚合统计
场景3:筛选特定Profile用户的评分数据
比如你想找出some_field等于某个值的用户,统计他们收到的评分分布:
from django.db.models import Count # 先筛选Profile,再关联评分记录并按评分值分组统计 target_profile_ratings = UserProfile.objects.filter(some_field="目标值").select_related('user').annotate( rating_distribution=Count('user__rated_user__rating', distinct=True) ).values('user__rated_user__rating', 'rating_distribution')
注意事项
- 你的
UserRating模型已经设置了unique_together = ("subject_user", "rating_user"),确保了同一个用户不会给另一个用户重复评分,所以聚合时不会出现重复统计的问题,这点很赞~ - 如果数据量较大,建议用
select_related(一对一/外键)或prefetch_related(多对多/反向外键)来减少数据库查询次数,避免N+1问题
内容的提问来源于stack exchange,提问作者Bartosz Bąk
相关产品推荐
相关产品推荐

