简体   繁体   English

Django 与 MySQL:'子查询返回超过 1 行'

[英]Django with MySQL: 'Subquery returns more than 1 row'

Using django with a MySQL DB and given these models:将 django 与 MySQL DB 一起使用,并给出以下模型:

ModelB   ---FK--->   ModelA
    - ref_type
    - ref_id
 
ModelC

I want to get all the ModelC for each ModelA via an annotation.我想通过注释获取每个 ModelA 的所有 ModelC。

I tried many options looking at existing solutions but could not make it work.我尝试了许多查看现有解决方案的选项,但无法使其发挥作用。 The following code works when there is just one ModelC for each ModelA but as soon as there is more than one, I get the Subquery returns more than 1 row error and I don't know how to get a list of the ModelC models instead.以下代码在每个 ModelA 只有一个 ModelC 时有效,但只要有多个,我就会得到Subquery returns more than 1 row错误,并且我不知道如何获取 ModelC 模型的列表。 Ideally, I'd like to build a list of JSON objects of the ModelC.理想情况下,我想构建 ModelC 的 JSON 对象列表。

qs = ModelA.objects.all()

c_ids = (
    ModelB.objects \
        .filter(modela_id=OuterRef(OuterRef('id')), ref_type='c') \
        .values('ref_id')
)
all_c = (
    ModelC.objects \
        .filter(id__in=Subquery(c_ids)) \
        .values('id')
)

qs1 = qs.annotate(all_c=Subquery(all_c ))
for p in qs1:
    print(p, p.all_c)

ModelB looks like a junction table. ModelB 看起来像一个联结表。 Having an id pointing to A and C具有指向 A 和 C 的 id

Django supports junction tables. Django 支持连接表。

But when it comes to annotation the objects with the list of ids, I'm not entire sure if that is possible purely by the ORM.但是,当涉及到使用 id 列表注释对象时,我不完全确定 ORM 是否可能。

class ModelA(models.Model):
    model_c_objects = models.ManyToManyField("ModelC", through="ModelB") 

class ModelB(models.Model):
    model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
    model_b = models.ForeignKey(ModelB, on_delete=models.CASCADE)

class ModelC(models.Model):
    ...


# This one here I have no idea if it would work or not
ModalA.objects.prefetch_related("models_c_objects").annotate(model_c_object_ids=ArrayAgg("model_c_objects__id")

# If it doesn't:
class ModelA(models.Model):
    model_c_objects = models.ManyToManyField("ModelC", through="ModelB") 
    
    @property
    def model_c_object_ids(self):
        return list(self.model_c_objects.values("id", flat=True))

# And you can then use it like you wished
for model_a_object in ModelA.objects.prefetch_related("models_c_objects"):
    model_a_object.model_c_object_ids # list of model_c ids like: [1,4,12,63]

I'm feeling a bit lazy but either of the two solutions should work and they both use a single query.我感觉有点懒,但是这两种解决方案中的任何一种都应该可以工作,而且它们都使用一个查询。

I come from the assumption that Model B is indeed a through table for M2M relationship between Model A and Model C as Işık Kaplan suggested. I come from the assumption that Model B is indeed a through table for M2M relationship between Model A and Model C as Işık Kaplan suggested.

In Postgres you could use ArrayAgg like Işık Kaplan suggested.在 Postgres 中,您可以像 Işık Kaplan 建议的那样使用 ArrayAgg。 Equivalent in MySQL in GROUP_CONCAT but it is not present in the ORM out of the box.在 GROUP_CONCAT 中的 MySQL 中等效,但在开箱即用的 ORM 中不存在。 Also from personal experience I wouldn't recommend it as it performed terribly in my use case.同样从个人经验来看,我不会推荐它,因为它在我的用例中表现非常糟糕。

What I ended up doing was combining 2 queries using Python which was way faster then 1 complicated query with GROUP_CONCAT (around 60K records of "Model A" and 20K of "Model B" in my case).我最终做的是使用 Python 组合 2 个查询,这比使用 GROUP_CONCAT 的 1 个复杂查询要快得多(在我的例子中,大约有 6 万条“模型 A”的记录和 2 万条“模型 B”的记录)。 In your case it would look like this:在您的情况下,它看起来像这样:

a_qs = ModelA.objects.all()
c_ids_dict = defaultdict(list)
c_ids = a_qs.values("id", "models_c_objects__id")
for item in c_ids:
    if item["models_c_objects__id"]:
        c_ids_dict[item["id"]].append(item["models_c_objects__id"])
for p in a_qs:
    print(p, c_ids_dict.get(p.id, []))

The following should do以下应该做

from django.db.models.aggregates import Aggregate

class JSONArrayAgg(Aggregate):
   function = "JSON_ARRAYAGG"
   
ModelA.objects.annotate(
    all_c=Subquery(
        ModelB.objects.filter(
            ref_type="c",
            modela_id=OuterRef("id"),
        ).values(
            "modela_id"
        ).values_list(
            JSONArrayAgg("ref_id")
        )
    )
)

which translates to这转化为

SELECT
    model_a.*,
    (SELECT JSON_ARRAYAGG(model_b.ref_id)
     FROM model_b
     WHERE model_b.ref_type = "c" AND model_b.modela_id = model_a.id
     GROUP BY model_b.modela_id
    ) all_c
FROM model_a

But it would be much easier if you provided your exact model definition as it's likely only a matter of doing something along the lines of但是,如果您提供确切的 model 定义会容易得多,因为这可能只是按照以下方式做一些事情

ModelA.objects.annotate(
   all_c=JSONArrayAgg(
      "modelb_set__ref_id", filter=Q(modelb_set__ref_type="c")
   )
)

which translate to这转化为

SELECT
    model_a.*,
    JSON_ARRAYAGG(
       CASE WHEN model_b.ref_type = "c" THEN model_b.ref_id END
    )
FROM model_a
LEFT JOIN model_b ON (model_b.modela_id = model_a.id)
GROUP BY model_a.id

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 (1242,Djquo中的'子查询返回超过1行')错误? - (1242, 'Subquery returns more than 1 row') error in Django? 为什么 django queryset.extra() 抛出 OperationalError: (1242, 'Subquery returns more than 1 row')? - Why does django queryset.extra() throws OperationalError: (1242, 'Subquery returns more than 1 row')? Django ORM 由用作表达式的子查询返回的多行 - Django ORM more than one row returned by a subquery used as an expression DatabaseError:由子查询返回的多于一行用作表达式(Django) - DatabaseError: more than one row returned by a subquery used as an expression (Django) django视图错误:用作表达式的子查询返回的多行 - django view error: more than one row returned by a subquery used as an expression Django DatabaseError“子查询返回的多于一行用作表达式”对象的可编辑相关字段 - Django DatabaseError “more than one row returned by a subquery used as an expression” Editable related fields to object 为什么此子查询不能返回多行? - Why can't this subquery return more than one row? Django Unique返回的记录多于计数 - Django distinct returns more records than count 如何检查 Django 查询集是否返回多个 object? - How to check if Django Queryset returns more than one object? Django | 查询过滤器返回的结果多于应有的结果 - Django | Query filter returns more results than it should
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM