tachitomonn’s blog

IT技術関連の学習メモがメインでたまに趣味のこととか

Python Flask の基本

Python の軽量ウェブアプリケーションフレームワーク Flask の基本学習メモ。
公式ドキュメント
Welcome to Flask — Flask Documentation (1.1.x)
を元にお勉強。

使った環境はWin10およびPython3.7です。

インストール

venv でつくった仮想環境上で pip でインストールします。

>pip install Flask

Flask アプリケーションを動かす

アプリケーションを動かすには flask コマンドまたは python の -m スイッチを使う。
環境変数 FLASK_APP を設定して動かうアプリケーションを教えておく必要がある。

C:\selfstudy>set FLASK_APP=study_flask.py
(selfstudy) c:\selfstudy>flask run
 * Serving Flask app "study_flask.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

もしくは

(selfstudy) c:\selfstudy>python -m flask run
 * Serving Flask app "study_flask.py"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

http://127.0.0.1:5000/にブラウザからアクセスして確認することができる。
デバッグモードで起動するには環境変数 FLASK_ENV に development を指定してからサーバを起動する。
デバッグモードでは

  • デバッガーを使える
  • 自動リロードが使える

ので開発中は非常に便利です。

ルーティング

route()デコレーターを使って関数とURLの紐づけを行う。

可変URL

URL中にとしておくと関数側で任意の変数名で値を受け取ることができる。
で特定の型へコンバートして受け取ることもできる。

URL末尾のスラッシュの取り扱い

末尾のスラッシュを含めたURL指定をするとファイルシステム上のディレクトリへのアクセスのように扱われ、末尾スラッシュなしでアクセスしても末尾のスラッシュを付加してリダイレクトしてくれる。
一方、末尾のスラッシュなしでURL指定すると特定ファイルリソースへのアクセスのように扱われ、末尾スラッシュを付けてのアクセスは404エラーとなる。

URLのビルド

特定の関数からURLを逆引きするには url_for()関数を使う。
第1引数には関数名を受け付けて、オプションでキーワード引数を受け付ける(可変URL部分の変数名)。
未定義の変数を指定するとURLのクエリパラメータとして付加される。

HTTP メソッドのハンドリング

デフォルトではGETリクエストに対して応答するが、route()デコレーターのキーワード引数 methods で他のメソッドも扱うことが可能。

静的ファイル

static という名前のディレクトリを作ってアプリケーションのパッケージ内に置くか、モジュールと同ディレクリに置く。
アプリケーションからは /static で利用できる。
urlビルディングには特別な 'static' エンドポイント名を使う。

レンダリングテンプレート

Flask は Jinja2 テンプレートエンジンを使う。
render_template() メソッドにテンプレート名とテンプレートエンジンに渡したい変数をキーワード引数で渡す。
templates ディレクトリからテンプレートを探すのでアプリケーションのパッケージ内に置くか、モジュールと同ディレクリに置く。
テンプレート中では request, session, g といったオブジェクトにアクセス可能。
テンプレートは継承可能。

リクエストデータへのアクセス

request オブジェクトを通じてアクセスする。
request.method でリクエストメソッドに、 request.form でフォームデータへアクセスできる。
request.args から辞書ライクにURLパラメータの値へアクセスできる。
request.files から辞書ライクにアップロードファイルへアクセスできる。
アクセスしたファイルオブジェクトは save() メソッドで保存できる。
request.cookies から辞書ライクにクッキーへアクセスできる。

リダイレクトとエラー

リダイレクトには redirect() 関数を使う。
エラーコードとともにリクエストを中止させるには abort() 関数を使う。
デフォルトではエラーコードに応じた白黒のエラーページが表示される。
エラーページをカスタマイズするには errorhandler() デコレーターを使う。

JSONを提供するAPI

辞書を返すビューを作ればJSONレスポンスに変換してくれる。

ここまでで使ったスクリプトとテンプレート

#! /usr/bin/env python
# -*- coding: utf8 -*-

from flask import Flask, url_for, request, render_template, abort, redirect
from markupsafe import escape

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "Hello, World!"

@app.route("/bye")
def bye():
    return "Bye!"

@app.route("/user/<username>")
def show_user(username):
    return "User {username}".format(username=escape(username))

@app.route("/user_id/<int:user_id>")
def show_user_id(user_id):
    return "User ID {user_id:d}".format(user_id=user_id)

@app.route("/path/<path:subpath>")
def show_subpath(subpath):
    return "Subpath {subpath}".format(subpath=escape(subpath))

@app.route("/top/")
def top():
    return "The top page"

@app.route("/about")
def about():
    return "The about page"

@app.route("/url_building")
def show_url_for_result():
    result1 = url_for("hello_world")
    result2 = url_for("bye")
    result3 = url_for("show_user", username="test user")
    result4 = url_for("show_user", username="test user", user_id=1234)
    return "{}<br>{}<br>{}<br>{}<br>".format(
            result1, result2, result3, result4
            )

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        return "POST"
    else:
        return "GET"

@app.route("/css")
def show_css():
    return url_for("static", filename="style.css")

@app.route("/hello/")
@app.route("/hello/<name>")
def hello(name=None):
    return render_template("hello.html", name=name)

@app.route("/redirect")
def redirect_exsample():
    return redirect(url_for("redirect_to"))

@app.route("/redirect_result")
def redirect_to():
    abort(401)
    print("this is never executed")

@app.route("/redirect2")
def redirect_exsample2():
    return redirect(url_for("redirect_to2"))

@app.route("/redirect_result2")
def redirect_to2():
    abort(404)

@app.errorhandler(404)
def page_not_found(error):
    return "error page! 404!", 404

@app.route("/api_sample")
def sample_api():
    return {
            "key1":"value1",
            "key2":"value2",
            "key3":"value3",
            }
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

セッション

標準のセッション管理はクッキーを用いて実装されている。
このセッション管理をを使うには秘密鍵をセットする必要がある。
秘密鍵はできる限りランダムにするのが望ましい。
例えば os.urandom(16) などで作成するのも手。
標準のセッション管理はクライアント側で行うことになる。
サーバー側でセッション管理を行う場合はいくつかの Flask extension があるので使ってみるのも手。

セッションで使ったスクリプト

#! /usr/bin/env python
# -*- conding: utf8 -*-

from flask import Flask, session, redirect, url_for, request
from markupsafe import escape

app = Flask(__name__)

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route("/")
def index():
    if "username" in session:
        return "Logged in as {}".format(escape(session["username"]))
    return "You are not logged in"

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        session["username"] = request.form["username"]
        return redirect(url_for("index"))
    return """
    <form method="POST">
        <p><input type=text name=username>
        <p><input type=submit value=Login>
    </form>
    """

@app.route("/logout")
def logout():
    session.pop("username", None)
    return redirect(url_for("index"))