GAEのDataStoreで前後ページネーション
GAEでDataStoreを使っていてページネーションを作ろうとしたらつらみがあった。
なんとかCursorを使って前後ページネーションくらいは実装できたのでメモ。
DataStoreも一応クエリでoffsetを指定したり、countで件数を取得することはできるのだが、これらが実は全件を舐める実装となっているらしく、気軽には使えない。
つまり総データ件数に比例してリソースや時間を食う処理になってしまうため、運用時間とともにデータが増えていくアプリでは死に繋がる機能となっている。
なのでCursorを使ってうまいことする。
main.py
@app.route('/articles', methods=['GET'])
def article_list():
POSTS_PER_PAGE = 20
cursor = request.args.get('cursor')
current_cursor = Cursor(urlsafe=cursor)
# 表示する要素を取得するクエリ 作成時間降順
query = Article.query().article(-Article.timestamp)
articles, next_cursor, more = query.fetch_page(POSTS_PER_PAGE, start_cursor=current_cursor)
if not (cursor):
# 最初のページは引数のcursorがなく、前のページもない
has_prev = False
else:
# 最初のページ以外は前のページがある ソート順を逆にして前のページのCursorを取得
prev_query = Article.query().article(Article.timestamp)
prev_articles, prev_cursor, prev_more = prev_query.fetch_page(POSTS_PER_PAGE, start_cursor=current_cursor)
has_prev = True
# 前へのリンクを表示するためのURL引数
if (has_prev and prev_cursor):
prev_cursor_urlsafe = prev_cursor.urlsafe()
else:
prev_cursor_urlsafe = ''
# 次へのリンクを表示するためのURL引数 moreがなければ次はない
if (more):
next_cursor_urlsafe = next_cursor.urlsafe()
else:
next_cursor_urlsafe = ''
return render_template('article_list.html',
articles = articles,
prev_cursor = prev_cursor_urlsafe,
next_cursor = next_cursor_urlsafe,
)
templates/article_list.html
<div class="row" style="margin-top: -10px;">
<div class="col-md-1">
{% if prev_cursor %}
<a href="/articles?cursor={{prev_cursor}}" class="btn btn-default">前へ</a>
{% endif %}
</div>
<div class="col-md-10">
</div>
<div class="col-md-1" style="text-align: right;">
{% if next_cursor %}
<a href="/articles?cursor={{next_cursor}}" class="btn btn-default">次へ</a>
{% endif %}
</div>
</div>