Django 13. QuerySet API 2

jiffydev·2020년 11월 5일
0

들어가기 전에

이 포스팅에서 사용한 데이터들은 이곳이 원 출처임을 밝혀 둔다.
한편 쿼리셋 메소드는 종류가 많고 그 기능도 여러가지이므로 여기서 다 다루기는 어렵다. 이 포스팅에서는 기본적인 메소드 위주로 언급하고, 언급되지 않은 메소드에 대해서는 공식문서를 확인하면 더 자세히 알 수 있다.

새 QuerySet을 반환하는 메소드 2탄

1. order_by(*fields)

가져온 인스턴스들의 순서를 내가 원하는 기준에 따라 바꿀 수 있다. 매개변수로 테이블의 필드를 주면 필드의 값에 따라 기본적으로 오름차순으로 정렬한 쿼리셋을 리턴한다. 필드는 하나 이상의 매개변수를 가질 수 있고, 첫 번째 필드에서 같은 순서가 나타나면 두 번째 필드를 기준으로 정렬하게 된다.

>>> orderby=Product.objects.all()
>>> orderby.order_by('price').values('price')
<QuerySet [{'price': 10298}, {'price': 11129}, {'price': 11320}, {'price': 12538}]>
>>> orderby.order_by('-price').values('price')
<QuerySet [{'price': 179120}, {'price': 108410}, {'price': 99166}, {'price': 98579},]>

필드를 기준으로 오름차순 정렬과 '-'를 붙여 내림차순 정렬이 가능하다.

만약 참조하고 있는 테이블이 있다면, 그 테이블의 필드를 기준으로도 정렬할 수 있다. 참조 테이블의 필드를 가져올 때는 '__'(double underscore)를 사용한다.

>>> relation_order=OrderedProduct.objects.values('related_product__id', 'id')
>>> relation_order.order_by('related_product__id')
<QuerySet [{'related_product__id': 1, 'id': 158}, {'related_product__id': 1, 'id': 161}, {'related_product__id': 1, 'id': 173}, {'related_product__id': 1, 'id': 188}]>

OrderedProduct가 참조하는 Product 테이블의 id를 기준으로 정렬되었다.

2. select_related(fields) & prefetch_related(lookups)

장고를 처음 쓸 때는 성능 신경 안쓰고 무조건 all()로 다 가져오고, 그 안의 참조하는 테이블도 all()로 가져오지만, 시간이 지나면 이와 같은 방법이 매우 비효율적이라는 것을 깨닫게 된다.

Json으로 데이터를 프론트에 줄 때는 하나만 주는 것이 아니라, all()로 가져온 인스턴스들을 반복문 속에서 하나씩 필드를 참조해 값을 가져오게 된다. 그런데 필드 중에 다른 테이블을 참조하는 필드가 있다면 그 필드를 가져올 때마다 데이터베이스를 조회해야 한다. 데이터가 1억 건이 있다면 조회도 1억번 해야한다는 뜻이다. 이러한 중복 조회는 성능에 엄청난 영향을 미치므로 최대한 데이터베이스를 조회할 일을 줄여야 한다.

select_related는 FK를 가진 필드나 1:1 관계의 데이터에서, 데이터베이스를 조회할 때 쿼리문을 하나 추가하여 참조하는 테이블의 데이터도 함께 가져오는 역할을 한다. 함께 가져온 데이터는 캐시에 저장되어 있어 참조할 때도 데이터베이스에 직접 접근할 필요가 없어지므로, 공식문서의 표현을 빌리자면 performance booster가 된다.

사용 방법은 간단한데, get/filter/all 등을 사용하기 전이나 후에 select_related('참조하려는 필드명')를 붙여주면 된다.(순서는 상관 없다) 프로젝트를 하면서 겪은 사항으로는, all이나 filter처럼 복수의 인스턴스를 가져오는 경우, 참조하는 필드의 id 순서대로 정렬된다는 점이 있다. get처럼 하나만 가져오는 경우는 크게 문제되지 않을 것이다.

select_related가 1:1혹은 1:N(FK) 관계에서 사용할 수 있다면, prefetch_related는 N:N 관계에서 사용된다. 이 메소드는 각 'lookup'에 대해 관련된 모든 객체를 한 batch에 가져온다. 덕분에 select_related와 마찬가지로 데이터베이스에 hit하는 횟수를 줄여주어 탐색 속도를 높인다.

이 둘의 차이는 테이블의 JOIN 방법에서 나타나는데, select_related는 SQL의 JOIN문을 사용해 한 번의 INNER JOIN으로 쿼리셋을 가져온다. 한편 prefetch_related는 각각의 관계에 대해 따로 쿼리문을 실행에 데이터를 찾아 오고, 파이썬에서 JOIN을 수행한다. 그렇기 때문에 select_related는 쿼리문을 한 번만 수행하여 속도가 빠르지만 그 특성상 many 관계의 데이터는 가져오지 못하는 반면, prefetch_related는 각 관계의 수만큼 쿼리문을 수행하기 때문에 상대적으로 속도는 느릴 수 있지만 many 관계의 데이터도 가져와서 저장할 수 있다. 따라서 이 둘을 얼마나 적절히 쓰냐가 쿼리의 중복을 줄이는데 관건이 될 것이다.

조금 옛날 글이기는 하지만 초보몽키님의 블로그에서 직접 쿼리문을 몇번 사용하는지, 중복이 몇개인지 확인할 수 있으므로 참고.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글