Flaskでflask-paginateを使って簡単にページネーションを実装する

データベースから取出したデータをFlaskで表示するのに、すべてのレコードを1ページに表示するとすごく見にくいので、1ページ内に表示する件数を指定できるように、ページネーション(ページ割)を実装します。ページネーションを1からプログラミングするのはすごく難しそうなので、flask-paginateを導入し簡単にページネーション機能を使います。

ファイルの階層

flaskpaginate-demo
├── app.py
├── sampledata.sqlite3
├── static
│   ├── css
│   │   └── semantic.min.css
│   └── js
└── templates
    └── paginate-sample.html

今回作成するページを表示するだけなら、render_templateやjsは必要ないのですが、後で拡張しやすいようにtemplatesディレクトリを作成し、render_templateを使用しています。

flask-paginateは、CSSフレームワークのSemantic UIに対応しているので、CSSはSemantic UIを使用します。

完成イメージ

  • 1ページに50件のデータを表示
  • CSSは、フレームワークのSemantic UIを使用

sqlite3でデータベースを作成

2021年の京都府地価公示データ(https://www.land.mlit.go.jp/landPrice/AriaServlet?MOD=2&TYP=0)を元にして、sqlite3で作成したデータベースsampledata.sqlite3に接続します。

CREATE TABLE sample (region, address, date, price integer, price_per integer, rosenka integer, chiseki integer, rate numeric, kenpei integer, youseki integer, station, distance integer);

こんな感じでsampleテーブルを作成しました。

カラム名内容
region地域名text
address所在地番text
date鑑定評価日text
price評価額integer
price_per1㎡当たりの価格integer
rosenka路線価integer
chiseki地積integer
rate年間変動率numeric
kenpei指定建ぺい率integer
youseki指定容積率integer
station交通施設(最寄駅)text
distance交通施設(最寄駅)からの距離integer
sqlite> .mode csv
sqlite> .import ./sampledata.csv sample

事前に作成しておいたsampledata.csvを、sqlite3のsampleテーブルに読み込んで作成しています。

私が作成したCSVファイルは、著作権の関係で再配布できるかわからないので、必要なデータは自身で作成してください。

ソースコード

flask-paginateをインストール

pip install flask-paginate

Flaskをインストールしていない場合は、インストールしておきます。

pip install Flask

flask-paginate 0.6.0 documentationを参考にflask-paginateを設定していきます。

app.py

app.py
import sqlite3
from flask_paginate import Pagination, get_page_parameter
from flask import Flask,request,render_template

app = Flask(__name__)

# sqlite3のデータベースに接続
con = sqlite3.connect('sampledata.sqlite3')
cur = con.cursor()

# 1平米当たりの価格が高い順にレコードを検索
cur.execute('select * from sample order by price_per desc')

# 検索結果をすべて取得(リスト)
results = cur.fetchall()

cur.close()
con.close()

@app.route('/')
def index():
    # 表示しているページのページ番号を取得
    # default=1を変更することで、デフォルトで表示するページを変更
    page = request.args.get(get_page_parameter(), type=int, default=1)

    # 1ページに表示させるレコードの件数を指定(今回は50件)
    rows = results[(page - 1)*50: page*50]

    # page:現在のページ
    # total:すべてのレコード件数
    # per_page:1ページに表示させるレコードの件数
    # css_framework:CSSフレームワーク(bootstrap,foundation,semantic,bulmaに対応)
    pagination = Pagination(page=page, total=len(results),  per_page=50, css_framework='semantic')

    return render_template('paginate-sample.html', rows=rows, pagination=pagination)

if __name__=='__main__':
    app.run()

ページ番号を取得

page = request.args.get(get_page_parameter(), type=int, default=1)
  • 表示しているページのページ番号を取得
  • default=1を変更することで、最初に表示するページを変更可

1ページに表示する件数を指定

rows = results[(page - 1)*50: page*50]
  • 1ページに表示させるレコードの件数を指定(今回は50件に設定)

Pagination()のパラメーター

pagination = Pagination(page=page, total=len(results),  per_page=50, css_framework='semantic')
page現在のページ
totalすべてのレコード数
per_page1ページに表示させるレコードの件数
css_framework使用するCSSフレームワーク
(bootstrap,foundation,semantic,bulmaに対応)

paginate-sample.html

paginate-sample.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="./static/css/semantic.min.css">
  <title>Document</title>
</head>
<body style="margin: 20px;">
  <h1 class="ui header center aligned block inverted blue">
    2021年 京都府地価公示
    <div class="sub header">1㎡当たりの価格 降順</div>
  </h1>
  <div>
    <!-- レコード情報を表示 -->
    {{ pagination.info }}
    <!-- ページネーションを表示 -->
    {{ pagination.links }}
  </div>
  <table class="ui striped celled compact table inverted grey">
    <thead>
      <tr class="center aligned">
        <th>#</th>
        <th>地域</th>
        <th>所在地番</th>
        <th>評価日</th>
        <th>評価額</th>
        <th>1㎡当たりの価格</th>
        <th>路線価</th>
        <th>地積</th>
        <th>変動率</th>
        <th>建ぺい率</th>
        <th>容積率</th>
        <th>最寄駅</th>
        <th>距離</th>
      </tr>
    </thead>
    <tbody>
      <!-- レコードを1件ずつ処理 -->
      {% for row in rows %}
        <tr>
          <!-- レコード番号を表示 -->
          <td>{{ loop.index + pagination.skip }}</td>

          <!-- レコードの内容を整形 -->
          {% for content in row %}
            <!-- 所在地番を左揃え -->
            {% if loop.index == 2 %}
              <td class="left aligned">{{ content }}</td>
            <!-- 評価額・1平米当たりの価格・路線価を3桁区切りにして右揃え -->
            {% elif loop.index > 3 and loop.index < 7 %}
              <td class="right aligned">{{ "{:,}".format(content) }}</td>
            <!-- その他は中央揃え -->
            {% else %}
              <td class="center aligned">{{ content }}</td>
            {% endif %}
          {% endfor %}

        </tr>
      {% endfor %}

    </tbody>
  </table>

  <!-- ページネーションを中央揃えで表示 -->
  <div style="text-align: center;">
    {{ pagination.links }}
  </div>

  <div style="margin: 50px;">
    <p>出典:「国土交通省地価公示・都道府県地価調査」(国土交通省) (https://www.land.mlit.go.jp/landPrice/AriaServlet?MOD=2&TYP=0)</p>
    <p>「国土交通省地価公示・都道府県地価調査」(国土交通省) (https://www.land.mlit.go.jp/landPrice/AriaServlet?MOD=2&TYP=0)を加工して作成</p>  
  </div>

</body>
</html>

レコード情報を表示

{{ pagination.info }}

【displaying 1 – 50 records in total 628】といった、レコード情報を表示します。

ページネーションを実装

{{ pagination.links }}

こんな感じ(CSSは、SemanticUIを使用)でページネーションを表示します。

レコード番号を表示

{{ loop.index + pagination.skip }}

何件目のレコードかレコード番号を表示します。

設置デモ

https://dattesar.com/flaskpaginate-demo/