クエリの最適化を学習しようと思い簡単なwikiサイトの作成に取り組み10万件のデータを入れて色々試してみました。
Django Debug Toolbarを見てみると
ListViewでそのデータの件数を数えている部分が時間がかかっているようなので
なんらかの方法で短く出来ないか模索してみました。
そもそもカテゴリー分けしてカテゴリー一覧を表示させたほうがいいのかもしれませんが
あくまで学習として取り組んでみました。
こんなmodelがある前提で書いていきます。
本当はもっとフィールドや外部キーがあるのですが内容と直接関係ないので省略してます。
class Wiki(models.Model):
title = models.CharField(max_length=100, verbose_name="タイトル")
created = models.DateTimeField(auto_now=True, verbose_name="作成日時")
updated = models.DateTimeField(auto_now_add=True, verbose_name="更新日時")
def __str__(self):
return self.title
やったこと
wikiの件数を数字で保存するmodelを作って
既存のPagenatorを継承したクラスを作って
単純にその数字を返すようにしました。
wikiを保存する毎に+1する感じです。
もともとデータを数える部分は40ms程かかっていたのですが
結果としては1.3ms程になりました。
modelの作成
class Count(models.Model):
number = models.PositiveIntegerField(verbose_name="個数")
created = models.DateTimeField(auto_now=True, verbose_name="作成日時")
updated = models.DateTimeField(auto_now_add=True, verbose_name="更新日時")
def __str__(self):
return str(self.number)
こんなmodelを作ります。
Pagenatorの作成
├── app
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── requirements.txt
├── static
├── templates
│ ├── base.html
│ └── wiki
│ └── index.html
└── wiki
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── pagenators.py #ここ
├── tests.py
├── urls.py
└── views.py
pagenators.pyを作成します。
上記のような構成です。
ファイルを編集していきます。
from django.core.paginator import Paginator
from django.utils.functional import cached_property
from .models import Count
class WikiPaginator(Paginator):
@cached_property
def count(self):
count = Count.objects.values_list('number', flat=True)[0]
return count
Djangoのソースコードを見てPagenatorクラスのcount関数の返す値を
先ほど作ったmodelの値を返すようにしました。
@cached_property親クラスのcount関数でも使われていたのでそれにならってつけています。
まだよくわかってないのでそれについてはもうちょっと学習してみようと思います。
view
from django.views.generic import ListView
from .pagenators import WikiPaginator
class IndexView(LoginRequiredMixin, ListView):
template_name = 'wiki/index.html'
model = Wiki
context_object_name = "wikis"
paginate_by = 10
paginator_class = WikiPaginator
paginator_classを指定出来るので先ほど作成したWikiPaginatorを指定します。
あとはWikiの作成時に例えばform_validを使ってCountのnumberに+1するようにすればオッケーです。
最後に
もっといい方法やご指摘があったら教えていただけたら幸いです。
参考URL
https://github.com/django/django/blob/main/django/core/paginator.py
https://github.com/django/django/blob/main/django/views/generic/list.py