この例の問題の1つは、 queryset.count()
を使用できないことです。 .count()
であるため、サブクエリとして クエリセットを評価してカウントを返そうとします。
したがって、正しいアプローチは Count()
を使用することだと思うかもしれません。 代わりは。多分このようなもの:
Post.objects.annotate(
count=Count(Tag.objects.filter(post=OuterRef('pk')))
)
これは2つの理由で機能しません:
-
タグ
querysetはすべてのTag
を選択します フィールド、Count
1つのフィールドのみをカウントできます。したがって:Tag.objects.filter(post =OutsideRef('pk'))。only('pk')
必要です(tag.pk
を頼りに選択するには 。 -
カウント
それ自体はサブクエリ
ではありません クラス、Count
Aggregate
です 。したがって、Count
によって生成された式Subquery
として認識されません (OutsideRef
サブクエリが必要です)、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
が原因です 集計関数です。現在、結果には影響しませんが、他の場合には影響する可能性があります。そのため、 docs
集計がsubquery
に移動される別のアプローチを提案します 値
の特定の組み合わせを介して +注釈
+値コード> :
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"