들어가며
처음 video list 페이지를 들어갈 때마다, video의 개수가 늘어나면서 로딩시간이 눈이띄게 길어진 것을 느낄 수 있었다. 그래서 찾게된 QuerySet 효율적으로 사용하기!
초보몽키의 개발공부로그를 통해 웹 성능을 향상시킬 수 있었다.
기존의 문제점
기존의 video list view를 보면 다음과 같은 방법으로 모든 video를 불러오고 이를 template로 넘긴다.
video_list = Video.objects.all() # 모든 video 불러오기
return render(request, 'video/video_list.html', {'video_list':video_list}) # template에 넘기기
그리고 django-debug-toolbar을 통해 queries를 확인해보면, 15 queries와 13 similar, 그리고 7개의 중복이 있는 것을 확인할 수 있다.
select_related와 prefetch_related
- ForeignKey, OneToOneField와 같은 Single-valued relationships에만 사용
- SQL JOIN을 사용하여 모든 field를 SELECT문으로 포함시킴으로써 related objects를 같은 DB 쿼리로 한번에 가져온다.
- ManyToMany, OneToMany을 포함한 모든 relationships에 사용 가능
- 각각의 relationship에 대해 별도의 쿼리를 날리고 Python으로 'joining'을 한다.
그럼 이제 Model과 Template을 살펴보자.
Model
class Video(models.Model):
class Category(models.TextChoices):
Music = 'music'
Movie = 'movie'
Drama = 'drama'
Comedy = 'comedy'
Information = 'info'
Daily = 'daily'
Beauty = 'beauty'
Art = 'art'
Book = 'book'
Sport = 'sport'
Food = 'food'
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
video_key = models.CharField(max_length=12)
likes_user = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='likes_user')
upload_date = models.DateTimeField(auto_now_add=True, null=True) # first created date
category = models.TextField(choices=Category.choices, blank=True)
Template
<div class="row">
{% for video in video_list %}
<div id="video_list_search" class="col-md-5" style="float: left; margin-bottom: 30px; margin-right: 30px;">
<a href="{% url 'video_detail' pk=video.id %}">
<img src="http://img.youtube.com/vi/{{video.video_key}}/mqdefault.jpg" alt="thumbnail">
<h4>
{{ video.title }}
</a>
<span class="glyphicon glyphicon-heart" style="color:red"></span>{{ video.count_likes_user }}
</h4>
<h5>{{ video.upload_date }} by.{{ video.author }}</h5>
</div>
{% endfor %}
</div>
QuerySet 수정하기
문제1: auth_user 중복
auth_user이 반복되는 것은, auth_user을 담는 field와 관련하여 쿼리를 수정해야겠다고 생각했다.
그래서 우선 author field를 선택하였고, 이는 ForeignKey이기에
쿼리셋을 다음과 같이 수정하였다.
video_list = Video.objects.select_related('author').all()
그랬더니, (15 queries)->9개로, (13 similar, 7개의 중복)->6 similar로 바뀌었다!
문제2: likes_user INNER JOIN similar
likes user은 ManyToManyField이기에, prefetch_related를 사용하여
최종적으로 다음과 같이 수정하였다.
video_list = Video.objects.prefetch_related('likes_user').select_related('author').all()
그랬더니, 총 4개의 쿼리로 마무리할 수 있었다!
마무리하며
DB 쿼리를 어떻게 쓰느냐에 따라 이렇게 성능에 영향을 많이 받는다는 것을 직접 실감하게 되었다.
앞으로 DB 설계에서 쿼리공부도 많은 노력을 쏟아야 나중에 덜힘들다는 사실을 알았고,
debug tool을 잘 사용해야겠다는 생각을 하였다!
'Python > Django' 카테고리의 다른 글
[Docker+Django] Dockerfile로 Docker Django 이미지 만들기 / django 2.0 버전 이상 (0) | 2020.05.05 |
---|---|
[Django Test] Django Unit Test (업데이트 중...2020/04/28) (0) | 2020.04.28 |
[Error Solved][Django url] django 프로젝트 앱에 <str:category> url을 추가했을 때 모든 url이 해당 url로 라우팅 되는 문제 (0) | 2020.04.28 |
[Django template] template 확장하기 / base.html 분리를 통해 html 재사용성 향상 (0) | 2020.04.27 |
[Django] form을 활용하여 login view 수정하기 (0) | 2020.04.25 |