具有OuterRef的简单子查询

我正在尝试使用一个非常简单的Subquery方法OuterRef(不是出于实际目的,而是为了使其正常工作),但是我一直遇到相同的错误。

posts/models.py

from django.db import models

class Tag(models.Model):

name = models.CharField(max_length=120)

def __str__(self):

return self.name

class Post(models.Model):

title = models.CharField(max_length=120)

tags = models.ManyToManyField(Tag)

def __str__(self):

return self.title

manage.py shell

>>> from django.db.models import OuterRef, Subquery

>>> from posts.models import Tag, Post

>>> tag1 = Tag.objects.create(name='tag1')

>>> post1 = Post.objects.create(title='post1')

>>> post1.tags.add(tag1)

>>> Tag.objects.filter(post=post1.pk)

<QuerySet [<Tag: tag1>]>

>>> tags_list = Tag.objects.filter(post=OuterRef('pk'))

>>> Post.objects.annotate(count=Subquery(tags_list.count()))

最后两行应为我提供每个Post对象的标签数量。在这里,我不断收到相同的错误:

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery.

回答:

您的示例的问题之一是您不能queryset.count()用作子查询,因为它.count()试图评估查询集并返回计数。

因此,人们可能会认为正确的方法是使用Count()替代方法。也许是这样的:

Post.objects.annotate(

count=Count(Tag.objects.filter(post=OuterRef('pk')))

)

这不能工作有两个原因:

  1. 查询集Tag选择所有Tag字段,而Count只能依靠一个字段。因此:Tag.objects.filter(post=OuterRef('pk')).only('pk')是必需的(选择依靠tag.pk)。

  2. Count本身不是一个Subquery类,Count而是一个Aggregate。因此,由生成的表达式Count不能识别为SubqueryOuterRef需要子查询),我们可以使用来解决Subquery

应用1)和2)的修复程序将产生:

Post.objects.annotate(

count=Count(Subquery(Tag.objects.filter(post=OuterRef('pk')).only('pk')))

)

如果您检查正在生成的查询:

SELECT 

"tests_post"."id",

"tests_post"."title",

COUNT((SELECT U0."id"

FROM "tests_tag" U0

INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id")

WHERE U1."post_id" = ("tests_post"."id"))

) AS "count"

FROM "tests_post"

GROUP BY

"tests_post"."id",

"tests_post"."title"

您会注意到一个GROUP

BY条款。这是因为COUNT是聚合函数。现在它不会影响结果,但是在其他情况下可能会影响结果。这就是为什么文档提出了一种不同的方法的原因,该方法是subquery通过特定的values+

annotate+组合将聚合移动到中values

Post.objects.annotate(

count=Subquery(

Tag.objects

.filter(post=OuterRef('pk'))

# The first .values call defines our GROUP BY clause

# Its important to have a filtration on every field defined here

# Otherwise you will have more than one group per row!!!

# This will lead to subqueries to return more than one row!

# But they are not allowed to do that!

# In our example we group only by post

# and we filter by post via OuterRef

.values('post')

# Here we say: count how many rows we have per group

.annotate(count=Count('pk'))

# Here we say: return only the count

.values('count')

)

)

最终将产生:

SELECT 

"tests_post"."id",

"tests_post"."title",

(SELECT COUNT(U0."id") AS "count"

FROM "tests_tag" U0

INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id")

WHERE U1."post_id" = ("tests_post"."id")

GROUP BY U1."post_id"

) AS "count"

FROM "tests_post"

以上是 具有OuterRef的简单子查询 的全部内容, 来源链接: utcz.com/qa/412786.html

回到顶部