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

Django多对多关联注解SQLite正常,MariaDB报子查询多行错误

Django ManyToMany字段注解在MariaDB下的多值子查询错误解决

问题场景

模型定义如下:

class Link(models.Model):
    products = models.ManyToManyField(Product, related_name = "%(class)s_name", related_query_name = "product_link_qs", blank = True)
    position = models.ForeignKey(Position, on_delete = models.CASCADE)

class Position(models.Model):
    place = models.PositiveIntegerField(unique = True) 
    store = models.ForeignKey(Store, on_delete = models.CASCADE)

class Store(models.Model):
    name = models.CharField("name", max_length = 32)

在Admin中尝试通过注解关联Store名称以实现排序功能:

@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
    list_display = ["product", "get_store"]
    list_filter = ["position__store"]

    ### extend by product_link_qs related name property to make field sortable in the admin
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.annotate(storename = Product.objects.filter(product_link_qs = OuterRef("id")).values("store__name"))

    @admin.display(description = "store name", ordering = "storename")
    def get_store(self, obj):
        return obj.storename or None

现象:

  • 当单个Link仅关联一个Product时,测试环境SQLite和生产环境MariaDB均正常运行。
  • 当单个Link关联多个Product时,SQLite仍正常,但MariaDB抛出错误:django.db.utils.OperationalError: (1242, 'Subquery returns more than 1 row')

错误原因

SQLite对单值注解的子查询有宽松处理:当子查询返回多行时,会自动取第一条结果填充注解字段。但MariaDB严格遵循SQL标准,不允许返回多行的子查询赋值给单个字段,因此触发1242错误。

解决方案

方案1:直接通过关联关系获取Store名称(最优)

Link本身关联Position,Position关联Store,无需绕Product查询,直接通过外键链取值即可避免子查询问题:

@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
    list_display = ["product", "get_store"]
    list_filter = ["position__store"]

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        # 直接通过外键关联注解Store名称
        return qs.annotate(storename=F('position__store__name'))

    @admin.display(description = "store name", ordering = "storename")
    def get_store(self, obj):
        return obj.storename or None

方案2:限制子查询返回单条结果(若业务必须从Product取)

如果业务逻辑要求从关联的Product中获取Store名称,需确保子查询仅返回一条结果,可使用切片配合Subquery:

from django.db.models import Subquery, CharField

@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
    list_display = ["product", "get_store"]
    list_filter = ["position__store"]

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        # 子查询限制返回第一条结果
        product_store_subquery = Subquery(
            Product.objects.filter(product_link_qs=OuterRef("id"))
            .values("store__name")[:1],
            output_field=CharField()
        )
        return qs.annotate(storename=product_store_subquery)

    @admin.display(description = "store name", ordering = "storename")
    def get_store(self, obj):
        return obj.storename or None

也可使用Django 3.2+支持的聚合函数First:

from django.db.models import First

@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
    list_display = ["product", "get_store"]
    list_filter = ["position__store"]

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.annotate(
            storename=First('products__store__name')
        )

    @admin.display(description = "store name", ordering = "storename")
    def get_store(self, obj):
        return obj.storename or None

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:48:22