Kay のページング機能を使ってみた

はじめに

AppEngine/PythonCGM 系の Webサービスを作っているんですが、運営しているうちにデータ数が多くなりそうです。いや、なりますね。

1ページに何百何千もデータを表示すると、見づらいし表示が遅いので、ページングを実装しなければいけません。

Kay にはページング機能があるじゃないか

Kay Framework は バージョン 1.1 から、ページングを簡単に実装できる kay.utils.paginator モジュールが追加されました。

使い方はこんな感じ

こんなモデルがあるとします。
# -*- coding: utf-8 -*-
# core.models

from google.appengine.ext import db

class Entry(db.Model):
    title = db.StringProperty(required=True)
    content = db.TextProperty()
    created = db.DateTimeProperty(auto_now_add=True)
paginator モジュールを使ってページに表示するデータを作成
# -*- coding: utf-8 -*-
"""
core.views
"""

from kay.utils import render_to_response
from kay.utils.paginator import Paginator
from core.models import Entry

def index(request, number=1):
    entries = Entry.all().order("-created")

    # 1 ページに出力するデータの数は 5 つ
    paginator = Paginator(entries, 5)

    # 指定されたページを取り出す
    page = paginator.page(number)

    # テンプレートを使って出力
    data = dict(page=page)
    return render_to_response('core/index.html', data)
URL マッピングを忘れず追加

ページ番号の文字列を数値に変換する処理は Kay にやって欲しいので、エンドポイントを分けておきます。

# -*- coding: utf-8 -*-
# core.urls
# 

from kay.routing import (
  ViewGroup, Rule
)

view_groups = [
  ViewGroup(
    Rule('/', endpoint='index', view='core.views.index'),
    Rule('/page/<int:number>/', endpoint='show_page', view='core.views.index'),
  )
]
データ一覧とページネーションを出力するテンプレートを書きます
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Paginator Sample</title>
    </head>
    <body>
        <div id="entry_list">
            <!--ページに表示するデータのリストは object_list から取得できる-->
            {% for entry in page.object_list %}
            <div class="entry">
                <h2>{{ entry.title }}</h2>
                <div class="entry_body">
                    {{ entry.content }}
                </div>
            </div>
            {% endfor %}
        </div>

        <div id="pagination">
            <!--前のページへのリンクを出力-->
            {% if page.has_previous %}
            <a href="{{ url_for('core/show_page', number=page.previous_page_number) }}"></a>
            {% endif %}

            <!--現在のページ番号と総ページ数を出力-->
            <span>{{ page.number }} / {{ page.paginator.num_pages }}</span>

            <!--次のページへのリンクを出力-->
            {% if page.has_next %}
            <a href="{{ url_for('core/show_page', number=page.next_page_number) }}"></a>
            {% endif %}
        </div>
    </body>
</html>
これでページングの完成です

実行画面がこちら。
f:id:griefworker:20110610143242p:image
簡単ですね。

簡単だけど…

db.Query と組み合わせた場合、最大 1000 件までしかあつかえません。これはデータストアの制約だから仕方ないか。あと、ページを表示するたびにデータを全部取得して、そのページに表示するデータを取り出しているので、データ数が多いと遅いです。

まとめ

速度を気にする人、1000件以上データを扱いたい人は、結局独自でページングを実装する必要がありますね。

キーや一意のプロパティを使ってページングを行う方法が App Engine の公式サイトで紹介されています。

これが定番かな。

あと、結構前に話題になった、SkipList みたいなデータ構造にする手もあります。

こちらは実装が大変だ。