コンテンツにスキップ

コンポーネント@Flask

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. App

Flaskクラス

▼ Flaskクラスとは

WSGIアプリケーションの実行に関するメソッドを持つ。

クラスの引数に、グローバル変数の『__name__』あるいはエントリーポイントのパスを直接的に設定する。

環境変数のFLASK_APPで指定したエントリーポイントでは、必ずFlaskクラスのインスタンスを作成する必要がある。

from flask import Flask

app = Flask(__name__)

# src/app.py
# app = Flask(src)


routeメソッド

routeメソッド

Flaskクラスにエンドポイントを追加する。

from flask import Flask

app = Flask(__name__)

PREFIX_FOO = "foo"
@app.route("/{PREFIX}/show", methods=["GET"])
    def show():
        # DBへのアクセス処理

...


runメソッド

runメソッドとは

設定されたルーティングを元に、WerkzeugによるWebサーバーを起動する。

開発環境のみで推奨される方法である。

from flask import Flask

app = Flask(__name__)

PREFIX_FOO = "foo"
@app.route("/{PREFIX}/show", methods=["GET"])
    def show():
        # DBへのアクセス処理

app.run()

▼ 引数

引数 説明
debug エラーログをブラウザ上に表示するか否かを設定する。
host 受信する通信のIPアドレスを設定する。全てのIPアドレスの場合は、0.0.0.0とする。
load_dotenv dotenvパッケージを読み込むか否かを設定する。これを有効化すれば、他の引数は環境変数から設定できる。
port インバウンド通信を待ち受けるポート番号を設定する。
use_reloader ホットリロードを有効化するか否かを設定する。


02. 環境変数

App

▼ FLASK_APP

アプリケーションのエントリーポイントを設定する。

FLASK_APPの値 説明
module:name モジュール名 (ファイル名) とインスタンス名を設定する。
module:function() モジュール名 (ファイル名) と関数名を設定する。
module モジュール名 (ファイル名) を設定する。
file.py ファイル名を設定する。

Cookieヘッダーでセッションデータ (ペイロード + タイムスタンプ + 署名) を運ぶ時のキー名を設定する。

デフォルトでは、キー名はsessionになる。

# レスポンス
200 OK
---
Set-Cookie: session=*****
# リクエスト
# レスポンスのSet-Cookieヘッダーによって、Cookieヘッダーをつける必要がある
GET /foo/
---
Cookie: session=*****

▼ SECRET_KEY

Cookieヘッダーでペイロードとタイムスタンプを署名するためのキーを設定する。


03. ベストプラクティス

アプリケーションファクトリーパターン

▼ アプリケーションファクトリーパターンとは

Pythonのコードを配置するディレクトリに__init__.pyファイルを配置し、ここでFlaskクラスのインスタンスを作成するメソッドを定義する。

# __init__.py
from flask import Flask

def create_app():
    app = Flask(__name__)

    @app.route('/')
    def index():
        return 'Hello World'

    return app

▼ エントリーポイント

プロジェクトのルートディレクトリに、create_appメソッドを実行するエントリーポイント (例:main.pyファイル) を配置する。

名前空間を判定する条件分の外でcreate_appメソッドを実行しないと、uwsgiがapp変数を見つけられない。

from src import create_app

app = create_app()

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

▼ 開発環境と本番環境の違い

本番環境では、アプリケーションの実行にrun関数とflask runコマンドを使用しないようにする。

代わりに、uWSGIやgunicornを使用して、エントリーポイントの関数を直接的にコールする。

本番環境と開発環境を同様にするために、本番環境のみでなく開発環境でもコマンドを使用しないようにしても良い。


04. ビルトイン関数

requests

▼ get

HTTPリクエストを送信する。

from flask import Flask

app = Flask(__name__)

def getProductReviews(product_id, headers):
    for _ in range(2):
        try:
            url = reviews['name'] + "/" + reviews['endpoint'] + "/" + str(product_id)
            res = requests.get(url, headers=headers, timeout=3.0)
        except BaseException:
            res = None
    if res and res.status_code == 200:
        request_result_counter.labels(destination_app='reviews', response_code=200).inc()
        # 200ステータスの場合、そのままブラウザにレンダリングする
        return 200, res.json()
    elif res is not None and res.status_code == 403:
        request_result_counter.labels(destination_app='reviews', response_code=403).inc()
        # 403ステータスの場合、ユーザーにわかりやすいメッセージに変えて、ブラウザにレンダリングする
        return 403, {'error': 'Please sign in to view product reviews.'}
    elif res is not None and res.status_code == 503:
        request_result_counter.labels(destination_app='reviews', response_code=503).inc()
        # 503ステータスの場合、ユーザーにわかりやすいメッセージに変えて、ブラウザにレンダリングする
        return 503, res.json()
    else:
        status = res.status_code if res is not None and res.status_code else 500
        request_result_counter.labels(destination_app='reviews', response_code=status).inc()
        # 500ステータスの場合、ユーザーにわかりやすいメッセージに変えて、ブラウザにレンダリングする
        return status, {'error': 'Sorry, product reviews are currently unavailable.'}

@app.route('/')
def home():
    product_id = 0
    headers = getForwardHeaders(request)
    user = session.get('user', '')
    product = getProduct(product_id)

    # detailsサービスにリクエストを送信する
    detailsStatus, details = getProductDetails(product_id, headers)
    logging.info("[" + str(detailsStatus) + "] details response is " + str(details))

    # reviewsサービスにリクエストを送信する
    reviewsStatus, reviews = getProductReviews(product_id, headers)
    logging.info("[" + str(reviewsStatus) + "] reviews response is " + str(reviews))

    # いずれかのマイクロサービスでアクセストークンの検証が失敗し、401ステータスが返信された場合、ログアウトする
    if detailsStatus == 401 or reviewsStatus == 401:
        logging.info("[" + str(401) + "] access token is invalid.")
        redirect_uri = url_for('logout', _external=True)
        return redirect(redirect_uri)

    response = app.make_response(render_template(
        'productpage.html',
        detailsStatus=detailsStatus,
        reviewsStatus=reviewsStatus,
        product=product,
        details=details,
        reviews=reviews,
        user=user))

    return response

session

▼ セッションデータの作成

セッションデータを作成し、レスポンスのSet-Cookieヘッダーに保管する。

from flask import Flask, session

app = Flask(__name__)

session['username'] = user

▼ セッションデータの取得

レスポンスのSet-CookieヘッダーやリクエストのCookieヘッダーから、セッションデータを取得する。

from flask import Flask, session

app = Flask(__name__)

session['username'] = request.form['username']
username = session['username']

session.get関数でも取得でき、デフォルト値を設定できる。

from flask import Flask, session

app = Flask(__name__)

session.get('username', 'None')

▼ セッションデータの保持期間

app.permanent_session_lifetimeで、セッションデータの保持期間を設定できる。

from flask import Flask, session

app = Flask(__name__)

app.permanent_session_lifetime = timedelta(days=5)

セッションデータを作成する前にsession.permanentを有効化しておくと、セッションデータの保持期間を無期限にできる。

この場合、保持期間は無視される。

from flask import Flask, session

app = Flask(__name__)

session.permanent = True
session['username'] = user


url_for

▼ external

ホスト名とデフォルトのプロトコル名 (HTTP) のあるURLを作成する。

from flask import Flask, url_for

app = Flask(__name__)

with app.test_request_context():
    # http://localhost/
    print(url_for('index', _external=True))

▼ scheme

ホスト名と指定したプロトコル名のあるURLを作成する。

from flask import Flask, url_for

app = Flask(__name__)

ith app.test_request_context():
    # https://localhost/
    print(url_for('index', _external=True, _scheme='https'))