https://docs.djangoproject.com/en/3.0/ref/models/querysets/#filteredrelation-objects
auth_user : socialaccount_socialaccount → 1 : 0 or 1 관계이다.
auth_user를 left로 해서 socialaccount_socialaccount의 특정컬럼 조회하기는?
>>> from django.contrib.auth.models import User
>>> from django.db.models import F, FilteredRelation
1. "SocialAccount객체의 소문자 + __ + SocialAccount객체의 속성인 provider" 조합으로 provider라는 alias지정하면 알아서 reverse lookup 된다.
>>> qs = User.objects.annotate(provider=F("socialaccount__provider"))
>>>
>>> for u in qs:
... print("{} : {}".format(u.username, u.provider))
...
[DEBUG] [2020-06-21 02:32:42,406] (0.000) SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined", "socialaccount_socialaccount"."provider" AS "provider" FROM "auth_user" LEFT OUTER JOIN "socialaccount_socialaccount" ON ("auth_user"."id" = "socialaccount_socialaccount"."user_id"); args=()
administrator : None
axxxxxx12 : kakao
axxxxxx : naver
user : google
jangxxx : google
jxpark : None
문법 : 메인 테이블명.objects.annotate(별명=F("조인할 테이블명 소문자__조인할 테이블에 있는 컬럼명"))
사실 설명은 테이블명이라 했지만 Django소스상 객체명이다.
2. FilteredRelation를 이용하여 Right쪽 테이블 SocialAccount의 특정 컬럼의 조건을 걸 수 있다.
# SocialAccount 라는 alias를 지정하여 조회
>>> qs = User.objects.annotate(SocialAccount=FilteredRelation('socialaccount')).filter(SocialAccount__provider='google')
>>> qs
[DEBUG] [2020-06-20 12:34:50,939] (0.000) SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" INNER JOIN "socialaccount_socialaccount" SocialAccount ON ("auth_user"."id" = SocialAccount."user_id") WHERE SocialAccount."provider" = 'google' LIMIT 21; args=('google',)
<QuerySet [<User: user>, <User: jangwon>]>
# alias지정없이 조회. 결과는 같다.
>>> qs = User.objects.filter(socialaccount__provider='google')
>>> qs
<QuerySet [<User: user>, <User: jangwon>]>
[DEBUG] [2021-05-01 15:33:50,607] (0.000) SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" INNER JOIN "socialaccount_socialaccount" ON ("auth_user"."id" = "socialaccount_socialaccount"."user_id") WHERE "socialaccount_socialaccount"."provider" = 'google' LIMIT 21; args=('google',)
3. 1번과 2번을 조합하면...
>>> qs = User.objects.annotate(SocialAccount=FilteredRelation('socialaccount')).annotate(provider=F("SocialAccount__provider"))
>>> for u in qs:
... print("{} : {}".format(u.username, u.provider))
...
[DEBUG] [2020-06-21 02:41:37,599] (0.000) SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined", SocialAccount."provider" AS "provider" FROM "auth_user" LEFT OUTER JOIN "socialaccount_socialaccount" SocialAccount ON ("auth_user"."id" = SocialAccount."user_id"); args=()
administrator : None
axxxxxx12 : kakao
axxxxxx : naver
user : google
jangxxx : google
jxpark : None
결과는 1번 쿼리와 같다.
FilteredRelation은 지정된 condition으로 조인하여 얻은 집합을 Alias로지정하고 이지정된 Alias집합으로 다시 조건으로 조회하도록하여 연속된 left outer join조건을 순차적으로 접근하여 성능을 높일수 있다.