IT/WEB

[Django] 테이블(모델)의 JOIN(2) ; select_related()

개발자 두더지 2020. 11. 16. 23:43
728x90

 지난번에 작성했던 '테이블(모델)의 JOIN(1)'의 경우 너무 가볍게 보기도 하고 거기서 언급했듯 스스로도 이해를 못했기 때문에 prefetch_related()와 select_related() 각각의 메소드 사용법을 조금 더 살펴보겠습니다. 이번 포스팅에서는 둘 중 먼저 select_related()의 사용 대해 다뤄보고자 합니다.

 

간단 사용법


쿼리를 실행했을 때에 지정된 외래 키의 개체도 함께 가져온다. 이를 통해 DB에 접근하는 횟수를 줄일 수 있다. 사용예에 대해서 살펴보자면, 첫 번째 코드를 두 번째로 코드로 변경할 수 있다.

# DB에 접근
e = Entry.objects.get(id=5)

# 아직 DB를 건들이고 있다.
b = e.blog

 

# DB에 접근
e = Entry.objects.select_related('blog').get(id=5)

# 미리 DB에서 얻어낸 객체를 참고하므로 DB를 계속해서 접근하지 않는다.
b = e.blog

 

 

조금 더 자세히 살펴보기


● 외래키의 외래키를 얻을 경우

다음과 같이 도시, 작가, 기사 모델이 있다고 가정하자.

from django.db import models

class City(models.Model):
    # ...
    pass

class Author(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Entry(models.Model):
    # ...
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

이 상황에서 기사의 모델의 고향(hometown)을 알고 싶은 상태이다. 먼저 select_related를 사용하지 않는 패턴을 살펴보자.

b = Entry.objects.get(id=4)  # DB에 접근
p = b.author         # DB에 접근
c = p.hometown       # DB에 접근

 3번 DB에 접근하게 된다. 계속해서 select_related를 사용한 패턴은 아래와 같다.

# 한 번의 쿼리로 author와 hometown 테이블로 부터 객체를 취득한다.
b = Entry.objects.select_related('author__hometown').get(id=4)
p = b.author         # 취득한 객체를 참고
c = p.hometown       # 취득한 객체를 참고

select_related를 사용하면 DB에 접근하는 것은 1번 뿐이다. 

 

● 관계 테이블의 리스트를 클리어

 selecte_related로 취득한 관계 테이블의 객체를 쿼리셋으로부터 클리어하고 싶을 때가 생길 수 가 있는데, 이때 키워드 인수에 None을 넣어 실행하면 된다.

without_relations = queryset.select_related(None)

 

● 여러 개의 select_related()를 엮어 사용할 때

select_related()로 여러 테이블의 관계를 형성하고 싶을 때 아래와 같이 작성하는 것이 아니라

select_related('foo').select_related('bar')

이와 같이 작성한다.

select_related('foo', 'bar')

 

● 관계 테이블의 객체가 존재하지 않는 경우

어떤 상황인지에 대해서는 조금 더 자세히 설명하도록 하겠다.

# 기사의 작가를 확인하면
entry.author
# 작가는 "고릴라"이다.
>> <Author: 고릴라>

entry.author.pk
>> 1

# "고릴라"를 삭제한다.
Author.objects.get(pk=1).delete()
entry = Entry.objects.select_related('author').first()
entry
<Entry: 고릴라입니다. 블로그 시작합니다.>
entry.author == None
True

 객체가 삭제되었음에도 QuerySet를 실행시키면 에러가 아닌 None이 리턴된다는 사실을 잊지말자.

 

● 인수를 지정하지 않는 경우

인수를 지정하지 않으면 null = False의 외래키만 취득대상이 된다. 기본적으로 인수를 명시적으로 지정하도록 하자.

 

● 관계 테이블의 데이터를 얻고 싶은 경우

아무튼 관계 테이블의 데이터가 필요한 경우 __(언더 스코어 2개)를 사용하는 표현을 쓴다. 고릴라 객체의 name과 description을 취득해보자.

entry = Entry.objects.filter(pk=1).select_related(
     'author'
).values('author__name','author__description')

>> <AuthorQuerySet [{'author__name': '고릴라', 'author__description': '와일드합니다.'}]>

 


참고자료

mkai.hateblo.jp/entry/2018/11/05/100000

728x90