[번역] Django - select_related()

폐쇄맨·2020년 8월 23일
0

번역

목록 보기
4/5
post-custom-banner

장고 공식문서에 있는 내용을 번역해본다.

select_related()

select_related(*fields)

쿼리가 실행될 때 related 객체 데이터를 추가적으로 SELECT 해오고, 외래키 관계를 "따르는 (follow)" 하나의 QuerySet을 리턴한다. 당장은 복잡한 쿼리를 만들지만 나중에 외래키 관계에 따른 추가적인 데이터베이스 쿼리를 생성하지 않게되는 장점이 있다.

아래의 예시는 일반적인 lookup과 select_related() lookup 사이의 차이를 보여준다. 여기에 일반적인 lookup이 있다:

# 데이터베이스에 쿼리를 날린다.
e = Entry.objects.get(id=5)

# Entry와 관계를 맺고 있는 Blog 객체를 가져오기 위해 다시 데이터베이스에 쿼리를 날린다.
b = e.blog

그리고 여기 select_related() lookup이 있다:

# 데이터베이스에 쿼리를 날린다.
e = Entry.objects.select_related('blog').get(id=5)

# 데이터베이스에 쿼리를 날리지 않는다. e.blog는 이미 이전 쿼리에서 가져왔기 (prepopulated) 때문이다.
b = e.blog

어떤 객체의 queryset에도 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() 함수가 없었다면, 각 엔트리에 관계된 블로그를 fetch 하기 위해 각 루프 회차때 마다 데이터베이스 쿼리를 날려야한다.
    blogs.add(e.blog)

filter()select_related() 함수 chaining의 순서는 중요하지 않다. 아래의 두 쿼리셋은 동일하다:

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

비슷한 방법으로 외래키의 객체가 포함하는 객체를 쿼리하기 위해 외래키 객체를 참조할 수도 있다. 만약 다음과 같은 모델들이 있다면:

여기서 Book의 외래키는 Person. 외래키 Person의 객체는 City. 결국 City를 참조하기 위해 아래의 예시가 있다.

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 객체를 캐쉬한다.

# Book의 author 필드와 Person의 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)  # 쿼리를 날린다.
p = b.author                # 쿼리를 날린다.
c = p.hometown              # 쿼리를 날린다.

select_related() 에 전달될 필드 리스트를 통해 ForeignKey 또는 OneToOneField 관계를 참조할 수 있다.

select_related() 에 전달될 필드 리스트를 통해 OneToOneField 의 반대 방향도 참조할 수 있다. - 즉, OneToOneField 타입의 필드를 선언한 객체로 참조가 가능하다는 말이다. 그 필드의 이름을 명시하기 보다는, OneToOneField 타입의 필드를 선언할 때 related_name 옵션을 사용한다.

가끔은 객체 간의 관계가 복잡하게 얽혀있거나, 프로그래머가 그 모든 관계를 파악하지 파악하지 못하고 있을 때, select_related() 를 사용해야할 상황들이 있다. 이러한 경우에는 argument 없이 select_related() 를 호출한다. 그러면 찾을 수 있는 모든 non-null 외래키 데이터를 SELECT 하게 된다 - nullable 외래키들은 반드시 명시해야한다. 이것은 쿼리를 더 복잡하게 만들고 실제로 필요한 경우보다 많은 데이터를 반환할 수 있으므로 대부분의 경우 권장되는 방법은 아니다.

QuerySet에서 과거 select_related() 호출에 의해 추가된 related 필드들을 지워야하는 경우, None을 파라미터로 넘겨줄 수 있다.

without_relations = queryset.select_related(None)

select_related() 를 chaining 하여 호출하는 것도 가능하다. select_related('foo', 'bar')select_related('foo').select_related('bar') 과 동일하다.

profile
폐쇄맨
post-custom-banner

2개의 댓글

comment-user-thumbnail
2020년 8월 23일

감사합니다🙇

1개의 답글