[Flask]uuidを生成して専用URLを作成する

はじめに

PythonのWebアプリケーションフレームワークFlaskを使って、「消耗品買い物リスト」を作ってみるシリーズ第9回目です。

今回は、買い物リストページの基本部分と、トップページから買い物リストページを生成する機能を途中まで実装します。

前回まで

今回やること

買い物リストの画面表示とリスト作成の原型を作ります。

機能概要 : 専用URLの発行

初期リリース段階ではログイン不要にしたいという理由から、買い物リストごとに専用URLを発行し、URLで直接アクセスできる仕様にします。
URLを知っていれば誰でも見られますし、URLを紛失した場合はアクセスできなくなります。

  • 買い物リストを作成するとURLが発行される
  • そのURLにアクセスすると買い物リストが閲覧・更新できる

という機能です。

専用URLをどうやって発行するか

URLは/page/XXX(XXX部分がID)という構成にします。

ここで、発行時には、XXXとなるIDの部分がかぶらないように、一意にしておく必要があります。
色々と手法はあるものの、今回は定番かつ実装も楽なUUIDを採用します。

全体像

この機能の完成のためにざっとやることを書き出してみます。

  • [ ] 買い物リスト新規作成
    • [ ] 初期データの作成
      • [ ] リストIDの作成(連番)
      • [ ] UUIDの生成
    • [ ] データを保存
  • [ ] 買い物リスト表示
    • [ ] URLのuuidから対象データを検索・取得
    • [ ] ページの表示

こんな感じですかね。

この記事では、まずは
– UUIDを生成して返す
– URLからUUIDを取得してページ表示
を実装します。

今回やらないこと

DBへのデータ保存はまだやりません。

URLからUUIDを取得してページ表示 : FlaskのRouting

あらためて。

まずは、/page/XXXというURLが叩かれるとXXXの値を取得して表示できるようにします。

  • /page/XXXでアクセスすると、
  • XXXと画面に記載されたページが表示される

Flaskで実現する方法を調べてみるとすぐにいいのが見つかりました。

Quickstart — Flask Documentation (1.1.x)
より抜粋

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % escape(username)

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % escape(subpath)

もうこれをやればOKですね。

uuidを生成する : uuid.uuid4()

新しいページを生成するために、uuidを生成します。

生成には、Pythonにあるライブラリをそのまま使います。
実装が楽で助かる…

import uuid as _uuid

# uuid生成
uuid = str(_uuid.uuid4())

二行で済みました。(uuid4()はuuid型を返すのでstr型に変換してます)

以下の記事は処理の中身まで追っていて勉強になります。
[Python] UUIDを生成するuuid.uuid4()はどうやってUUIDを生成しているのか? | Developers.IO

サンプル

こんなコードにしました。

ロジック部分

app.py

メイン処理。/page/XXXでアクセスされるとshow_user_pageメソッドを呼び出します。

from flask import Flask, render_template, request, redirect, url_for
from markupsafe import escape
import uuid as _uuid

from classes import page as pg

app = Flask(__name__)

@app.route('/')
def index():
    title = "to buy list"
    return render_template('index.html', title=title)

@app.route('/page/<uuid>/')
def show_user_page(uuid):
    title = 'Your page'
    # show user_page from uuid
    uuid = '%s' % escape(uuid)

    page = select_page_info(uuid)

    return render_template('user_page.html', title=title, page=page)


def select_page_info(uuid):
    """uuidから対象ページのデータを取得
    Return:
        Page : ページクラスのインスタンス
    """
    page = pg.Page(uuid)
    return page


@app.route('/page/create')
def create_new_page():
    """新規ページを作成する
    """
    # uuid生成
    uuid = str(_uuid.uuid4())

    # レコード生成

    # ページ表示
    return show_user_page(uuid)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

classes/page.py

class Page:
    def __init__(self, uuid):
        self.uuid = uuid

ページ表示部分

template/application.html

基盤となるページです。

<!DOCTYPE html>
<html>
  <head>
    <title>{{ title }} | tobuylist</title>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0">
  </head>
  <body>
      <div>
          {% block body %}
          {% endblock %}
      </div>
  </body>
</html>

template/user_page.html

リスト表示用のページです。
ロジック部分で受け取ったuuidをそのまま表示する、極めて単純な処理です。

{% extends "application.html" %}
{% block body %}
<div>
    <h1>{{ title }}</h1>
    <div>
        My id is {{ page.uuid }}.
    </div>
</div>
{% endblock %}

template/index.html

トップページです。
Create pageというリンクで専用URLを発行します。

{% extends "application.html" %}
{% block body %}
<div>
    <h1>トップページ</h1>
    <div class="field">
        <a href='page/create'>Create new page</a>
    </div>
</div>
{% endblock %}

挙動

買い物リスト画面表示

Flaskを立ち上げて、/page/hogehogeにアクセスしてみます。

hogehogeという文字列を表示できました。

買い物リスト生成

トップページからCreate pageリンクをクリックしてみます。

uuidが生成され、それが画面内に表示されました。

気になる点

トップページからはCreate pageすると、URLがpage/createのままなので、page/XXXとなるように後々手を加えたいと思います。

最後に

画面表示の原型ができたので、さらにデータ保存や読み込みの機能を組み込んでいきます。

参考