2017-05-03 1 views
8

OuterRef를 사용하는 매우 간단한 하위 쿼리를 만들려고합니다 (실용적인 목적이 아니라 실제로 작동하도록 만들기 위해).하지만 동일한 오류가 계속 발생합니다.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로 이동 조금 다른 접근을하시기 바랍니다 그게 마지막이 생산됩니다

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]'를 태그 필터에 추가하면'django.core.exceptions.FieldError : Expression에 혼합 유형이 포함됩니다. output_field'를 설정해야합니다. – mjuk

+1

'queryset.query'를 출력하고'RDBMS'에서 직접 실행하여 여러분이 얻은 결과를 직접 볼 수 있습니다. 나는 어떤 행에 대해'Count'가 0 대신에'NULL'을 반환 할 것이라고 추측합니다. 행을 w/o하지 않은 행을 임시로 제외시킴으로써'.filter (count__gte = 1)'을 확인하려고 할 수 있습니다.그러나'Subquery'는 두 번째 인자 인'output_field'를 받아 들일 수 있습니다.'output_field = fields.IntegerField()' – Todor

+0

감사합니다. 그것은 내가 필요한 것입니다. – mjuk