Django使用Subquery报错返回多行,如何基于Flag计算用户评分?
解决Django子查询返回多行导致的ProgrammingError问题
模型结构
class ContentFlag(models.Model): user = models.ForeignKey( User, verbose_name="user", related_name="object_flags", on_delete=models.CASCADE, ) flag = models.CharField(max_length=30, db_index=True) content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE ) object_id = models.PositiveIntegerField() content_object = GenericForeignKey("content_type", "object_id") class Article(models.Model): owner = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_("owner"), null=True, blank=True, editable=True, on_delete=models.SET_NULL, related_name="owned_pages", ) flags = GenericRelation(ContentFlag, related_query_name="%(class)s_flags") title = models.CharField(max_length=255) class User(index.Indexed, AbstractUser): email = EmailField(verbose_name="email", unique=True, null=True, blank=True) name = models.CharField( blank=True, max_length=100, ) flags = GenericRelation(to="core.ContentFlag")
问题描述
需求是基于用户发布的文章收到的LIKEDIT_FLAG数量计算用户评分,编写的代码如下:
subquery_plus = ContentFlag.objects.filter( Q(article_flags__owner_id=OuterRef("id")), flag=LIKEDIT_FLAG, ).values("id") top_users = ( User.objects.annotate( rating_plus=Count(Subquery(subquery_plus)), ).order_by("-rating_plus") )
执行后报错:
django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression
问题原因
Subquery作为表达式使用时,要求子查询只能返回单行单列的数据,但原代码中subquery_plus返回的是符合条件的所有ContentFlag的id列表(多行数据),直接传入Count会导致数据库无法处理这种多行子查询。
解决方案
方案一:利用反向关联直接统计(推荐)
通过User与Article、ContentFlag的反向关联关系,直接在annotate中完成统计,代码更简洁高效:
from django.db.models import Count, Q top_users = User.objects.annotate( rating_plus=Count( 'owned_pages__flags', filter=Q(owned_pages__flags__flag=LIKEDIT_FLAG), distinct=True ) ).order_by('-rating_plus')
owned_pages是Article模型中owner字段的related_name,关联用户发布的所有文章owned_pages__flags关联文章对应的所有ContentFlag记录filter参数筛选出flag为LIKEDIT_FLAG的记录distinct=True避免同一文章的重复flag被多次计数(根据业务需求可调整)
方案二:修改子查询返回聚合结果
如果需要用子查询实现,可在子查询内部先完成计数聚合,确保返回单行数据:
from django.db.models import OuterRef, Subquery, Count from django.db import models subquery_plus = ContentFlag.objects.filter( article_flags__owner_id=OuterRef('id'), flag=LIKEDIT_FLAG ).values('article_flags__owner_id').annotate( count=Count('id') ).values('count') top_users = User.objects.annotate( rating_plus=Subquery(subquery_plus, output_field=models.IntegerField()) ).order_by('-rating_plus')
- 子查询中先按用户分组(
values('article_flags__owner_id')),再统计该用户对应的LIKEDIT_FLAG数量 - 最后只返回统计后的
count值,确保子查询每行只返回一个数字,符合Subquery的要求
内容的提问来源于stack exchange,提问作者Headmaster
相关产品推荐
相关产品推荐

