TIL50 - select_related & prefetch_related

select_related & prefetch_related


하기에 작성된 글의 출처는 장고 공식문서입니다

select_relatedprefetch_related의 정의와
사용했을 때 어떻게 돌아가는 지 설명 후, 해당 내용을 예시로 한 코드를 작성했습니다.

이번 원티드 프리온보딩 과정을 하며 처음으로 사용해보게 되어서
아직 문법에 서툴지만, 점차 익숙해져서 능숙하게 사용할 수 있을거라 생각합니다.

1. select_related(*fields)

OneToOneFieldForeignKey 관계에서 사용할 수 있으며,
OneToOneField의 경우 역참조에 사용 가능합니다.
즉, 정참조에는 ForeignKey를, 역참조에는 OneToOneField를 사용합니다.
단, OneToOneField의 경우 특정 컬럼명을 명시해야 하며, models.py에서
related_name을 사용해야 한다고 합니다.

select_related는 내가 가져올 모델과 *fields안의 테이블 or 테이블의 컬럼을
JOIN하여 교집합인 부분을 가져옵니다.

만약 select_related를 여러번 쓰고 싶다면, 두 가지 방법으로 표기 가능합니다.

1. select_related('class1', 'class2')
2. select_related('class1').select_related('class2')

예를 들어

class Author(models.Model) :
    name = models.CharField(...)
    ...
    
    class Meta :
        db_table = 'authors'

class Book(...) :
    author = models.ForeignKey(Author, ...)
    ...
    
    class Meta :
        db_table = 'books'

위와 같이 모델링을 하고 사용한다면

# DB hit 1번
b = Book.objects.select_related('author').get(id=1)

# DB hit X (이미 위에서 author의 정보를 불러왔으므로 hit하지 않음)
c = b.author

이와 같이, Book 모델에 한 번 접근을 하면 되는 것입니다.
select_relatedJOIN을 통해 관계된 정보를 불러왔기 때문입니다.


2. prefetch_related(*look_ups)

정참조관계에 있는ManyToManyFieldManyToOneField 에서 사용되고,
역참조관계에 있는 ForeignKey 관계에서 사용됩니다.

장고 공식문서에는 GenericRelation, GenericForeignKey
지원한다고 나와있는데, 이건 추후에 공부하며 포스팅할 예정입니다.

JOIN하여 데이터를 가져오는 select_related와 달리,
prefetch_related*look_ups로 지정된 모델을 불러오는 쿼리를
한 번 더 실행합니다.

prefetch_related의 경우, 'joining' in Python이라는 문구로 설명하고 있는데,
상위 모델에서 쿼리를 1차로 실행하고, 상위 모델에서 받아온 ID를 통해
prefetch할 모델에 접근하여 두 번째 쿼리를 실행합니다.

예를 들어 아래와 같이 모델링을 한 뒤,

class Topping(...) :
    name = models.CharField(...)
    
    class Meta :
        db_table = 'toppings'

class Pizza(...) :
    name = ...
    toppings = models.ManyToManyField(Topping)
    
    class Meta :
        db_table = 'pizzas'

피자는 토핑을 정참조하고 있습니다.
그러면 토핑에게 피자는 역참조 관계입니다.

select_related를 통해 피자마다 토핑이 어떻게 들어있는지 접근하려면

pizza = Pizza.objects.select_related('topping')

으로 코드를 작성하면 됩니다.

만약 코드 작성을 이렇게 한다면

toppings = Topping.objects.prefetch_related('pizza_set').get(id=1)

토핑 테이블에서 id가 1인 정보를 가져온 뒤,
pizza로 접근하여 1번 토핑을 사용하는 피자가 어떤 게 있는지 접근할 수 있게 됩니다.


후기

처음 사용해봐서 그런지 정말 세밀하게 까진 아직 감이 안옵니다.

계속 써보면서 Django ORM 최적화를 할 수 있는 개발자가 되어야 할 것 같습니다.

좋은 웹페이지 즐겨찾기