간단 사용법
QueryAPI에서도 QuerySet을 돌려주는 메소드이다. select_related()와 마찬가지로 한 번의 쿼리로 관계 테이블의 객체를 얻을 수 있다 (즉 JOIN가능하다). 차이점이라면 prefetch_related()는 M2M 관계일 때 사용한다는 것이다. 이와 관련해서 조금 더 설명을 덧붙이자면 다음과 같다.
● select_relation()의 경우
select_relation()은 관계 테이블의 객체도 포함해서 SQL로 SELECT를 실행해 객체를 얻고 있다. 따라서 관계 테이블의 객체도 포함해서 한 번의 쿼리로 모든 대상 객체를 얻는다. 따라서 M2M의 관계의 객체를 대상으로 실행했을 경우 엄청난 수의 객체를 얻게 된다. 따라서 select_related는 ForeignKey나 one-to-one의 관계에만 사용하도록 장려하고 잇다.
● prefetch_related()의 경우
한편, prefetch_related()을 살펴보면 관계 테이블의 객체를 취득하기 위해 별도의 쿼리는 사용한다. 그리고 취득한 객체와 관계된 테이블을 SQL이 아닌 Python을 사용하여 JOIN을 한다.
그리고 이 메소드는 GenericRelation과 GenericForeignKey도 서포트하고 있다. 그러나 select_related()가 지원하고 있는 ForeignKey나 one-to-one은 사용할 수 없다.
조금 더 자세히 살펴보기
● 캐시가 클리어된 경우
Django의 문서로 부터 코드를 가져왔다. 이 코드는 문제가 있는 코드이다.
>>> pizzas = Pizza.objects.prefetch_related('toppings')
>>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
잠깐 보면 제대로 prefetch_related()를 사용하여 낭비되는 쿼리없는 것 처럼 보이지만, 실제로 2행의 for문에서 DB에 몇 번이고 접근하고 있다. 그 이유는 QuerySet의 특징에 의해 새로운 체인 메소드를 기존과 다른 쿼리가 실행되기 때문이다. 여기서 filter()를 사용하고 있으므로 새롭게 prefetch되지 않은 pizza 객체가 for문으로 처리 되어 버리고 있다.
● 의도적으로 캐시를 클리어하고 싶은 경우
select_related()와 동일하게 키워드 인수에 None을 전달한다.
>>> non_prefetched = qs.prefetch_related(None)
● Prefetch() 객체를 사용하는 경우
Prefetch()의 기본 파라미터는 다음과 같다.
Prefetch(lookup, queryset=None, to_attr=None)
1) 사용법
Prefetch() 객체는 prefetch_related()를 컨트롤할 때 사용한다. lookup이라는 키워드 인수는 prefetch_related()의 lookup과 같다. 이번에도 공식 문서의 코드를 가져왔다.
>>> from django.db.models import Prefetch
>>> Question.objects.prefetch_related(Prefetch('choice_set')).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
2) queryset 설정할 경우
계속해서 query인수를 설정해보자. queryset을 설정하는 것으로 관계 테이블로써 참고하고 있는 객체를 엮는 것이 가능하다.
>>> voted_choices = Choice.objects.filter(votes__gt=0)
>>> voted_choices
<QuerySet [<Choice: The sky>]>
>>> prefetch = Prefetch('choice_set', queryset=voted_choices)
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: The sky>]>
3) to_attr을 설정할 경우
to_attr인수를 이용해 prefetch에 독자적인 속성을 부여할 수 있다.
>>> prefetch = Prefetch('choice_set', queryset=voted_choices, to_attr='voted_choices')
>>> Question.objects.prefetch_related(prefetch).get().voted_choices
<QuerySet [<Choice: The sky>]>
>>> Question.objects.prefetch_related(prefetch).get().choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
voted_choices를 설정하는 것으로 객체에 대해서 어떠한 관계의 관계 테이블인지를 보다 명확히 할 수 있다. 그리고 to_attr를 이용해 얻은 prefetched의 결과는 리스트에 보존된다. 이것은 기존의 prefetch_relation이 QuerySet인스턴스에 결과를 캐시했을 때 보다 더 빠른 속도로 처리할 수 있으므로 to_attr의 사용을 추천한다.
참고자료
'IT > WEB' 카테고리의 다른 글
console.log(); 외 다양한 디버그 방법 (0) | 2022.05.22 |
---|---|
XPATH 사용법·작성법 (0) | 2021.01.13 |
[Django] 테이블(모델)의 JOIN(2) ; select_related() (0) | 2020.11.16 |
[Vue.js] Vue.js 입문 (1) ; 기본 구문 (0) | 2020.11.13 |
[Django] 테이블(모델)의 JOIN(1) (0) | 2020.10.21 |