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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 07:58:38