如何在Django ORM中实现LEFT JOIN查询获取帖子及指定用户对应评分
是否需要修改现有模型
无需修改现有模型即可实现需求,仅推荐做两处可选优化,提升数据一致性:
- 修正
Rating模型rating字段的默认值:原字段为IntegerField,默认值写为字符串'1'存在隐式转换风险,建议改为整数1 - 给
Rating增加联合唯一约束,避免同一用户对同一内容产生多条重复评分记录
优化后的Rating模型参考:
class Rating(models.Model): user = models.ForeignKey(BucketUser, on_delete=models.CASCADE) content = models.ForeignKey(Content, on_delete=models.CASCADE) rating = models.IntegerField(choices=rating_choices, default=1) class Meta: constraints = [ models.UniqueConstraint(fields=['user', 'content'], name='unique_user_content_rating') ]
ORM 实现方案
推荐使用annotate+Subquery的写法,逻辑和你提供的子查询SQL完全对齐,性能稳定,取值方便:
from django.db.models import Subquery, OuterRef # 从GET请求获取传入的user_id,注意做类型转换和合法性校验 target_user_id = int(request.GET.get('user_id')) # 构造评分子查询 rating_subquery = Rating.objects.filter( content=OuterRef('content'), user_id=target_user_id ).values('rating')[:1] # 查询所有Post并注入对应用户的评分字段 post_list = Post.objects.annotate( current_user_rating=Subquery(rating_subquery) ).all()
使用说明:
- 遍历
post_list时,每个Post对象会新增一个current_user_rating属性 - 如果当前传入用户对该
Post关联的Content打过分,属性值为对应的评分,否则为None - 可通过
print(post_list.query)打印生成的SQL,确认和你预期的逻辑一致
如果你需要获取完整的评分对象而非仅评分值,可以使用Prefetch方案:
from django.db.models import Prefetch target_user_id = int(request.GET.get('user_id')) post_list = Post.objects.prefetch_related( Prefetch( 'content__rating_set', queryset=Rating.objects.filter(user_id=target_user_id), to_attr='current_user_rating_list' ) ).all()
取值时通过post.current_user_rating_list[0].rating获取评分即可。
内容的提问来源于stack exchange,提问作者Rajnish kumar
相关产品推荐
相关产品推荐

