반응형
SMALL
[Django][Python]QuerySet을 통해 알아보는 ORM의 특징
위의 링크는 Django ORM이 가지고 있는 문제를 정리하고, 발생할 수 있는 문제에 대해서 정리해두었다.
그중에서 오늘은 Lazy Loading을 통해서 생길 수 있는 문제를 해결하는 방법을 코드로서 구현해보려고 한다.
앞서 올린 글에서도 언급했지만 Django에선 지연로딩(Lazy Loading)으로 인해서 Query문을 여러차례 불러오는 실수를 범할 수 있다.
아래는 단적인 예시다.
def i_am_function_view3(request: WSGIRequest):
# User를 선언하는 시점에는 SQL이 호출되지 않음
users: QuerySet = User.objects.all()
# 0번째 User를 얻어오고싶어서 users쿼리셋은 SQL을 호출
first_user: User = users[0]
# 바로 윗줄에서 user1명밖에 가져오지 않아서 모든 user를 얻으려면 어쩔수 없이 다시 SQL을 호출해야함
user_list: List[User] = list(users)
# 직렬화 로직
user_list_dict: List[Dict[str, Any]] = [
model_to_dict(user, fields=('id', 'username', 'is_staff', 'first_name', 'last_name', 'email'))
for user in user_list
]
user_list_json_array: str = json.dumps(user_list_dict, indent=1, cls=DjangoJSONEncoder)
return HttpResponse(content=user_list_json_array, content_type="application/json")
위의 코드로 살펴보면, 첫줄에서 유저에 대한 정보 쿼리를 선언하고 2번째 줄에서 list()로 쿼리문을 다시 실행시켜 결국은 쿼리를 한번만 실행해서 처리할 문제를 2번에 나눠서 처리해야하는 실수를 범하고 있다.
위의 코드를 실제 Django SQL문에서 확인하면 아래와 같다.
이러한 쿼리중복 사용을 막기위해선 간단하게, 쿼리문 사용위치를 바꿔주어 캐싱하는 방식으로 수정할 수 있다.
해결책은 아래와 같다.(위의 코드와 차이점은 쿼리셋을 호출하는 지점의 위치만 다르다! 2번 3번째줄 주목!)
def i_am_function_view4(request: WSGIRequest):
# User를 선언하는 시점에는 SQL이 호출되지 않음
users: QuerySet = User.objects.all()
user_list: List[User] = list(users)
# 바로 위에서 users쿼리셋은 모든 user를 가져오는 SQL을 이미 호출함 따라서 0번째 user는 users쿼리셋에 캐싱된 값을 재활용함 (SQL호출 X)
first_user: User = users[0]
# 이 예제를 통해 배울점: 쿼리셋을 호출하는 순서가 바뀌는 것만으로도 QuerySet캐싱때문에 발생하는 SQL이 달라질수있다.
# 직렬화 로직
user_list_dict: List[Dict[str, Any]] = [
model_to_dict(user, fields=('id', 'username', 'is_staff', 'first_name', 'last_name', 'email'))
for user in user_list
]
user_list_json_array: str = json.dumps(user_list_dict, indent=1, cls=DjangoJSONEncoder)
return HttpResponse(content=user_list_json_array, content_type="application/json")
같은 코드지만 친절한 설명이 있는 자료도 함께 첨부했다.
여기서 배울점은 쿼리셋은 캐싱을 하기 때문에 SQL의 순서가 바뀌기만 해도 효율적으로 데이터를 다룰 수 있다는 점이다.
반응형
LIST
'프로그래밍 > Django' 카테고리의 다른 글
[Django] 멀티 DB 라우터설정 및 연동하기 (0) | 2022.05.11 |
---|---|
[Django][에러노트]raise ValueError, "No frame marked with %s." % fname (0) | 2022.05.07 |
[Django][Python]QuerySet N+1 prblem 해결하기 (0) | 2022.04.06 |
[Django][Python]QuerySet을 통해 알아보는 ORM의 특징 (0) | 2022.04.06 |
[Django][Python]Silk라이브러리를 활용한 Django 서버부하 프로파일링 설치 (0) | 2022.04.06 |