[django][ORM] subquery

Hyeseong·2020년 12월 26일
1

django

목록 보기
23/35

VanJess - Touch the Floor

VanJess 는 Ivana와 Jessica Nwokike 자매로 구성된 나이지리아 R&B 듀오에요.

YouTube에서 커버 곡을 시작으로 경력을 시작어요. 그중 Drake 의 "Headlines"의 대박을 친 후에 음악 분야에 진지한 경력을 쌓기로 결정했조.

2011년에는 전문적으로 일을 하려고 사람도 고용하고 스튜디오에서 녹음도 했다고 하네요.


subquery

SQL에서 쿼리 아래 또 쿼리문을 작성하는 것을 서브 쿼리라고 한답니다.
그렇다면 django 역시 subquery를 갖추어야 겠조?

auth_user모델과 OneToOne 관계로 연결된 UserParent모델이 있다고 할게요.

아래 코드로 UserParent모델에서 auth_user를 가진 행을 모두 찾을 수 있어요.

>>> from django.db.models import Subquery
>>> users = User.objects.all()
>>> UserParent.objects.filter(user_id__in=Subquery(users.values('id')))
<QuerySet [<UserParent: UserParent object (2)>, <UserParent: UserParent object (5)>, <UserParent: UserParent object (8)>]>

다른 예제를 볼게요.

Category모델의 각 행별로, 가장 선한 능력치를 가진 Hero 행을 구해 볼게요.

class Category(models.Model):
    name = models.CharField(max_length=100)


class Hero(models.Model):
    # ...
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)

    benevolence_factor = models.PositiveSmallIntegerField(
        help_text="How benevolent this hero is?",
        default=50
    )

queryset으로 우리가 구하려는 가장~ 선한 능력치를 가진 영웅을 찾아볼게요.

hero_qs = Hero.objects.filter(
    category=OuterRef("pk")
).order_by("-benevolence_factor")
Category.objects.all().annotate(
    most_benevolent_hero=Subquery(
        hero_qs.values('name')[:1]
    )
)

여기서 보지 못했던 OuterRef()가 보이네요. 일단은 넘어가고 추후에 다룰게요.

SELECT "entities_category"."id",
       "entities_category"."name",

  (SELECT U0."name"
   FROM "entities_hero" U0
   WHERE U0."category_id" = ("entities_category"."id")
   ORDER BY U0."benevolence_factor" DESC
   LIMIT 1) AS "most_benevolent_hero"
FROM "entities_category"

차근 차근 나눠서 살펴 볼게요.

hero_qs = Hero.objects.filter(
    category=OuterRef("pk")
).order_by("-benevolence_factor")

Hero모델의 항목들을 선한정도의값(benevolence_factor)에 따라 내림차순으로 정렬하여 선택해요. 그리고 category=OuterRef("pk")를 이용해 이 선택이 서브쿼리로 사용될 수 있도록 준비합니다.

그 뒤 most_benevolent_hero=Subquery(hero_qs.values('name')[:1]) 로 서브쿼리에 별칭을 붙여 Category 쿼리셋 안에서 사용합니다. 이 때, hero_qs.values('name')[:1] 는 서브쿼리에서 첫 번째 행의 name 필드를 구하는 코드입니다.

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글