아래의 강의를 듣다가 요약정리 해봤다.
https://www.youtube.com/watch?v=EZgLfDrUlrk
1. QuerySet을 통해 알아보는 ORM 특징
- Lazy Loading 지연로딩 : 정말 필요한 시점에 SQL을 호출한다.
#예시 views.py
#User를 선언하는 시점에는 퀴리셋에 지나지 않고 실제로 SQL호출을 하지 않는다.
query_set = User.objects.all()
#list()로 쿼리셋을 호출하였을때 실제 SQL이 실행된다.
user_list = list(query_set)
- 지연 로딩의 단점
불필요하게 SQL이 두번 호출된다.
query = User.objects.all()
User = query[0]#한명의 데이터를 가져오기 위해서 실행될 SQL문에 limit 1을 주어 한번 실행하고
list_user= list(query) #또 다시 목록을 얻기 위해 다시 SQL문을 호출한다.
#비효율 적이다.
- Caching : QuerySet 캐싱을 재사용
쿼리셋을 호출하는 순서가 바뀌는것 만으로도 QuerySet캐싱 때문에 발생하는 SQL이 달라질 수 있다.
query = User.objects.all()
list_user= list(query) #전체 목록을 얻기 위해 SQL문을 호출
#바로 위에서 모든 목록을 가져오는 SQL을 이미 호출하였으므로 User쿼리셋에서 캐싱된 값을 재활용(SQL호출 X)
User = query[0]
-Eager Loading 즉시로딩 : N+1 Problem
user가 100명이라면 SQL은 100+1번 호출된다.
query = User.objects.all()
for user in query:
#for문에서의 user는 user중에 한명이다
user.userinfo
#한명의 정보를 가져오기 위해 sql쿼리문 호출 되고 for문이 돌때마다 호출된다.
해결법
selected_related()와 prefetch_related라는 메서드 사용
selected_related() : 조인
prefetch_related() : 추가 쿼리
#views.py
Model.objects
.filer(조건)
.select_related('정방향 참조')
.prefetch_related('역방향 참조')
#sql
select * from 'Model' m
(inner or left outer) join '정방향 참조 필드' on 'r on m.r_id = r.id'
where 조건절;
select * from '역방향 참조 필드' where id in ('첫번째 쿼리결과 id 리스트');
- queryset 캐시를 재활용하지 못하는 querset 호출
.all()로 질의하면 result_cache를 재사용한다.
.filter()를 사용하면 cache를 재사용하지 않고 또 sql문이 발생한다.
#회사의 목록을 EagerLoading함
company_list = list(Company.objects.prefetch_related("product_set").all())
#아래 두개는 위에서 쿼리가 돌았기때문에 SQL을 호출하지 않는다.
company = company_list[0]
company.product_set.all()#여기까지 EagerLoading해서 SQL 발생 XXXX
company.product_set.filter(name="불닭볶음면")#여기서는 SQL발생한다.
#SQL을 발생시키지 않으려면 all을 사용해서 찾는 방식을 사용해야 한다.
fire_noodel_project_list = [product for product in company.prodect_set.all() if product.name="불닭볶음면"]
- RawQuerySet은 NativeSQL이 아니다.
원하는 SQL을 위해 .raw()를 사용하자
raw_query = Model.objects.raw("select * from model where")# raw()를 선언하면 RawQuerySet
query = Model.objects.filter #raw() 이외에는 전부 QuerySet을 반환한다.
.raw()를 사용하게 되면 아래와 같은 메서드들을 사용할 수 없다.
이것들을 전부 raw()에서 NativeSQL로 작성해 줘야한다.
.select_related()
FilteredRelation()
.annotate()
.order_by()
.extra()
[:10],,,[:2] 메인 쿼리에 limit 옵션을 걸 수 없다.
- 서브쿼리 발생 조건 : QuerySet In QuerySet
아직 company_queryset이 수행되기 전이기 때문에
서브쿼리로 발생하게 된다.
#company pk가 20이하인 Product들을 전부 조회
company_queryset = Company.objects.filter(id__lte=20).value_list("id", flat=True)
#서브쿼리로 발생하게 된다.
product_queryset = Product.objects.filter(product_owned_company__id_in = company_queryset)
서브 쿼리를 막기 위해서는
#list로 묶어서 바로 queryset을 수행시킨다.
company_queryset = list(Company.objects.filter(id__lte=20).value_list("id", flat=True))
product_queryset = Product.objects.filter(product_owned_company__id_in = company_queryset)
- values() values_list() 사용시 주의점
values() values_list()를 사용하게 되면 select_related(), prefetch_related() 옵션을 전부 무시한다.
values() values_list()를 사용하게 되면 DB Raw단위로 데이터를 반환한다.
즉 객체(object)와 관계지향(relational) 간에 매핑(mapping)이 일어나지 않는다.
'Django' 카테고리의 다른 글
[django/ubuntu] letsencrypt nginx에 인증서 적용하기 (0) | 2022.05.09 |
---|---|
[django/ubuntu]ubuntu에서 mysqlclient 설치 에러(패키지 설치 하나면 해결)/ERROR: No matching distribution found for Mysqlclient (0) | 2022.04.29 |
[django] session 사용법 (0) | 2022.04.25 |
[django]1대 다 join prefetch_related사용법(ORM방식) (0) | 2022.04.20 |
[django/에러]403 Forbidden CSRF검증에 실패하였습니다. 요청을 중단하였습니다. (0) | 2022.04.12 |