-
728x90
Select_related()
이 전에 잠깐 공부한 적이 있는
select_related
인데 깊게 공부하지 못 했던 것 같아 장고 공식문서를 보며 한 번 다시 공부해보려 한다.Select_related()란?
select_related()
는 쿼리가 실행될 때, 추가적인 관련 객체 데이터를 선택하고, 외래키 관계를 따르는 쿼리셋을 반환한다. 즉, 퍼포먼스 부스터의 역할을 하는데, 사용함으로써 당장은 보다 복잡한 쿼리를 만들지만, 나중에 외래키 관계를 사용할 때 추가적인 데이터베이스 쿼리를 필요로 하지 않는다는 장점이 있다.다음 예제는 평범한 쿼리 조회와
select_related()
조회가 어떻게 다른지를 설명해준다.기본 조회는 다음과 같다.
# 데이터 베이스 요청 e = Entry.objects.get(id=5) # 블로그 객체와 관련있는 값을 얻기 위해 다시 데이터베이스를 요청 b = e.blog
다음은
select_realted()
를 사용한 조회 예제이다# 데이터베이스 요청 e = Entry.objects.select_related('blog').get(id=5) # 이 부분에서는 데이터베이스에 요청하지 하지 않는다 # 왜냐면 e.blog는 전 쿼리에서 미리 채워져(가져왔기-prepopulated)있기 때문이다 # Doesn't hit the database, because e.blog has been prepopulated # in the previous query. b = e.blog
select_related()
는 객체의 어느 쿼리셋과도 사용할 수 있다.from django.utils import timezone # 향후 퍼블리시 되기로 예정되어 있는 블로그 각 항목들을 찾음 blogs = set() for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'): # 만약 select_related()를 사용하지 않았다면, 이 반복문은 각 반복마다 데이터베이스 쿼리를 사용한다. # 각 항목에 관련 블로그를 가져오기 위한 루프 반복 blogs.add(e.blog)
filter()
와select_related()
체이닝 순서는 중요하지 않다. 두 쿼리셋은 동일하다.Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog') Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())
다음과 같은 모델이 있는 경우, 외부키를 조회할 때와 비슷한 방법으로 외부키를 따를 수 있다.
from django.db import models class City(models.Model): # ... pass class Person(models.Model): # ... hometown = models.ForeignKey( City, on_delete=models.SET_NULL, blank=True, null=True, ) class Book(models.Model): # ... author = models.ForeignKey(Person, on_delete=models.CASCADE)
그러고나서
Book.objects.select_related('author__hometown').get(id=4)
를 호출함으로써 관련 Person 모델과 City 모델을 캐싱이 된다.# author과 hometown 테이블에 조인을 사용함으로써 한 번의 데이터베이스 요청을 보낸다. b = Book.objects.select_related('author__hometown').get(id=4) p = b.author # 데이터베이스 요청 없음 c = p.hometown # 데이터베이스 요청 없음 # select_related()를 사용하지 않는다면... b = Book.objects.get(id=4) # 데이터베이스 요청(1) p = b.author # 데이터베이스 요청(2) c = p.hometown # 데이터베이스 요청(3)
이와 같이
select_related()
에 전달된 필드 목록에서 외래키나 일대일 관계를 참조할 수 있다.또한, 전달된 필드 목록에서 역방향의 일대일 관계에도 참조할 수 있다 - 즉, 일대일 관계에서 다시 필드가 정의된 객체로 돌아갈 수 있다는 것이다.
필드 이름을 지정하는 대신, 관련 객체의 필드에
related_name
을 사용할 수 있다.select_related()
를 사용하다보면select_related()
를 다수의 관련 객체와 호출한다거나, 모든 관계를 인지하지 못한 상황이 있을 수가 있다. 이러한 경우 인자 없이select_related()
를 호출하는 것이 가능하다. 그렇게 하면, 찾을 수 있는 모든 non-null 외래키들이 선택되어진다(nullable 외래키는 반드시 명시되어야 한다).사실 이는 기본 쿼리를 더욱 복잡하게 만든다거나, 실제 필요한 데이터보다 더욱 많은 양의 데이터를 반환할 수 있기 때문에 대부분의 상황에서는 권장되지 않는 방법이다.
만약 쿼리셋에서 과거에 select_related를 호출함으로써 추가된 관련 필드의 리스트를 지워야하는 경우,
None
을 매개변수로 전달해줄 수 있다.>>> without_relations = queryset.select_related(None)
select_related를 이어서 호출하는 방식은 다른 메소드와 유사한 방식으로 작동한다.
즉,
select_related('foo', 'bar')
는select_related('foo').select_related('bar')
와 동일하다.select_related만으로도 글이 길어진 것 같아, 다음 글에서 prefetch_related를 다뤄볼까 한다.
Reference
728x90Jacob Lee🧑🏻💻Back-end to DevOps | 🎯Keep Running the Race
'Back-end > Django' 카테고리의 다른 글
Python Requests 한글 깨짐 문제 해결하기 (0) 2021.07.13 Django Admin 커스텀 레인지 필터 추가하기 (0) 2021.07.07 Django 유닛 테스트 with Pytest #1 (0) 2021.07.05 Pytest vs. Unittest (0) 2021.07.03 DRF 프로젝트에서 API 문서 자동화 하기 (drf-yasg) (0) 2021.05.30