LoginSignup
0
1

More than 1 year has passed since last update.

Djangoのページネーションの高速化に取り組んだ

Last updated at Posted at 2021-06-08

クエリの最適化を学習しようと思い簡単なwikiサイトの作成に取り組み10万件のデータを入れて色々試してみました。
Django Debug Toolbarを見てみると
ListViewでそのデータの件数を数えている部分が時間がかかっているようなので
なんらかの方法で短く出来ないか模索してみました。
そもそもカテゴリー分けしてカテゴリー一覧を表示させたほうがいいのかもしれませんが
あくまで学習として取り組んでみました。

こんなmodelがある前提で書いていきます。
本当はもっとフィールドや外部キーがあるのですが内容と直接関係ないので省略してます。

models.py
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の作成

models.py
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を作成します。
上記のような構成です。

ファイルを編集していきます。

paginators.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

views.py
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

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1