描画が遅くなってきたのでページングを実装した

当 Blog のタイトル一覧ギャラリーであるが、最初のうちは別に件数も少なかったので常に全件表示する感じでも全くストレスを感じなかった。 だがこの Blog は毎日欠かさず何かしら書いているため最近になると記事の件数は 600 件を超え、画像に関しては 300 件近くになってしまった。 その為さすがに全件表示してしまうと描画にそこそこ時間がかかるようになってきた。 そこで Web アプリでよくあるページング処理を実装してみたわけだが、大抵の Web フレームワークには Pagination の実装が内蔵されているが当 Blog で使用している Django にもあったので割と簡単に実装することができた。

データ取得処理の差し替え

例えば変更前のデータ取得コードが以下のようになっているとする:

context['posts'] = Post.objects.filter(**params)

これを Paginator オブジェクトでラップして必要なページを取得すれば良い:

paginator = Paginator(Post.objects.filter(**params), 100)  # 1 ページ 100 件とする
number_of_page = request.GET.get('page') if request.GET.get('page') else 1  # GET パラメータで来たページインデックス
context['posts'] = paginator.page(number_of_page)

これだけで GET パラメータで page=2 などと渡せば正しく 101 件目から 200 件目を描画してくれる。

ページング UI

後はページングの為の UI を描画する処理をテンプレートに書く。 公式ドキュメントにも書いてあるが PaginatorPage オブジェクトにはそれぞれページングの UI を描画するために使える値が含まれている。

paginator.count  # 検索対象総件数 (例えば 732 件中 101 から 200 件を表示している場合の「732」)
paginator.num_pages  # 総ページ数 (例えば 732 件で 100 件ずつの表示ならば「8」)
paginator.page_range  # [1, 2, 3, 4, 5, 6, 7, 8] などといった全ページ番号のリスト. これをテンプレート側で for 文で回して使う

page.has_next()  # 次ページがあるか
page.has_previous()  # 前ページがあるか
page.has_other_pages()  # 他のページがあるか (使うのか?)
page.next_page_number()  # 次ページ番号
page.previous_page_number()  # 前ページ番号
page.start_index()  # 開始件数 (例えば 732 件中 101 から 200 件を表示している場合の「101」)
page.end_index()  # 終了件数 (例えば 732 件中 101 から 200 件を表示している場合の「200」)

page.number  # ページ番号
page.paginator  # Page インスタンスから Paginator インスタンスを取得する. なので Paginator を Template 側に渡す必要はない

これを使って当 Blog では以下のようにページングを実装してみた (posts は View 側から渡された Page インスタンスである):

<div class="pagination-count">
    {{ posts.paginator.count }} 件中 {{ posts.start_index }} ~ {{ posts.end_index }} 件を表示しています。
</div>
{% if posts.paginator.num_pages > 1 %}
<div class="pagination">
    {% for i in posts.paginator.page_range %}
        {% if i == posts.number %}
    <em>{{ i }}</em>
        {% else %}
    <a href="?page={{ i }}{% if tag_id %}&tag_id={{ tag_id }}{% endif %}">{{ i }}</a>
        {% endif %}
    {% endfor %}
</div>
{% endif %}

ページング程度であればすべて自分で実装することもそこまで大変ではないが、フレームワークで用意されているページングの実装を使うとやはり記述するコードが減って楽だ。