2017-05-03 78 views
8

OuterRefを使用する非常に単純なサブクエリを作成しようとしていますが(実用的ではありません)、同じエラーが発生しています。最後の2行は、各ポストのオブジェクトのために私にタグの数を与える必要がありますOuterRefを使用した単純なサブクエリ

ポスト/ 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シェルコード

>>> 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())) 

。そして、ここで私は同じエラーを取得しておく:あなたの例で問題の

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

答えて

17

一つ.count()試みはクエリセットを評価して、カウントを返すことがあるため、あなたが、サブクエリとしてqueryset.count()を使用することができないということです。

正しいアプローチはCount()を代わりに使用することだと思うかもしれません。たぶん、このような何か:

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

二つの理由から、この文句を言わない仕事:Countは一つだけのフィールドをカウントすることができますしながら、

  1. Tagクエリセットは、すべてのTagのフィールドを選択します。したがって:Tag.objects.filter(post=OuterRef('pk')).only('pk')が必要です(tag.pkの数を選択するには)。

  2. Count自体は、CountAggregateSubqueryクラスではありません。したがって、Countによって生成された式がSubqueryとして認識されない場合は、Subqueryを使用して修正できます。

そして最終版は次のようになります。

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は凝集がvalues + annotate + values

Post.objects.annotate(
    count=Subquery(
     Tag.objects.filter(post=OuterRef('pk')) 
      .values('post') 
      .annotate(count=Count('pk')) 
      .values('count') 
    ) 
) 

の特定の組み合わせによってsubqueryに移動され、少し異なるアプローチを示唆しているThatsなぜ最後に、これは生成されます:

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" 
+0

おかげで、ということ働いた!しかし、 'pk__in = [1,2]'をTagフィルタに追加すると 'django.core.exceptions.FieldError:Expressionには混合型が含まれています。 output_field'を設定する必要があります。 – mjuk

+1

'queryset.query'を表示して' RDBMS'で直接実行して、何を返すのかを見ることができます。私はいくつかの行 'Count'は0ではなくNULLを返すかもしれないと思います。一時的に行を除外して、すなわち.filter(count__gte = 1)を確認することができます。しかし、 'Subquery'は2番目の引数を受け取ります。これは' output_field'です。 'output_field = fields.IntegerField()' – Todor

+0

に感謝します。 – mjuk

関連する問題