Blow Up by Black Swan

Python-Flask の使い方(クイックスタート編 Part.4)

クイックスタート編最後となるPart.4です。Part.4ではレスポンスオブジェクトやセッション、ロギングなど、主要機能と覚えておいた方が良い機能が中心となります。

・各記事の記載内容
Part.1 -> 1.最小アプリケーション~3.デバッグモード
Part.2 -> 4.ルーティング
Part.3 -> 5.スタティックフォルダ〜7.リクエストデータへのアクセス
Part.4 -> 8.リダイレクトエラー〜最後

公式ドキュメント: Quickstart – Flask Documentation

※なお当記事は 2019 年 9 月現在の最新バージョンである Flask1.1.x の公式ドキュメントに基づいています。今後の Flask や Python のアップデートによっては記事内容に何らかの齟齬が生じることもありますので、事前にバージョンを確認頂くか、適宜公式ドキュメントを参照頂ければと思います。

8. リダイレクトとエラー

ユーザを他のエンドポイントにリダイレクトさせる場合、redirect()メソッドを使用します。エラーコードでリクエストを中断する場合はabort()メソッドを使います。

# コード例
from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))    #loginページへリダイレクト

@app.route('/login')
def login():
    #401のエラーコードをユーザに送信
    abort(401)  # 401...アクセスの拒否
    this_is_never_executed()

初期設定では、それぞれのエラーコードに対して、白黒のエラーページが表示されます。エラーページをカスタマイズする場合は、errorhandlerデコレータを使用します。render_template()はデフォルトでステータスコード200を返します(参照: https://flask.palletsprojects.com/en/1.1.x/errorhandling/#error-handlers )。

  • redirect( endpoint )…ユーザーを他のエンドポイントに送るためのメソッド
  • abort( statu_code )…リクエストを、エラーコード(404など)を付帯して中断するためのメソッド
  • app.errorhandler()…エラーページをカスタマイズするデコレーター
#app.errorhandler
from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404    #エラーページ用のhtmlを読み取り、ステータスコードに404をもつ

9. レスポンスオブジェクト

view関数(HTMLなどを返す、routeでデコレータされた関数)からの戻り値は、自動的にレスポンスオブジェクトに変換されます。戻り値が文字列の場合、レスポンスボディの文字列と200のステータスコード、”text/heml”MIMEタイプを持ったレスポンスオブジェクトに変換されます。戻り値が辞書型の場合、レスポンスを生成するためにjsonify()が呼び出されます。Flaskがview関数の戻り値をレスポンスオブジェクトに転換するロジックは次のようになります。

  1. 正確な型のレスポンスオブジェクトが返された場合、直接view用の関数から返される
  2. 戻り値が文字列型の場合、そのデータとデフォルトのパラメータを持ったresponseオブジェクトが作られる
  3. 戻り値が辞書型の場合、jsonify()を使ってレスポンスオブジェクトが作られる
  4. タプルが返された場合、タプルのアイテムを使って追加情報を渡すことができる。タプルは、(response, status)(response, heders),(response, status, headers)の形式である必要がある。statusの値は、設定されているステータスコードを上書きし、headersには、追加のヘッダーの値を持ったリスト、または辞書データを渡すことができる。
  5. 上記のどれにも当てはまらない場合は、Flaskは戻り値が有効なWSGIアプリケーションと仮定して、その戻り値をresponseオブジェクトに転換する

view関数内で戻り値となるレスポンスオブジェクトを取得したい場合、make_response()メソッドを利用します。

#単純なview関数とレスポンスオブジェクトを変更する場合の方法
@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

# 上記のview関数がレスポンスオブジェクトを修正する場合
@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)  # responseオブジェクトを明示的に生成
    resp.headers['X-Something'] = 'A value'    #ヘッダーに値を追加
    return resp    #ヘッダーを修正したresponseオブジェクトを返す

9-1. API with JSON

APIを記述するときの一般的なレスポンスフォーマットはJSONです。FlaskではそのようなAPIを簡単に記述することができます。view関数から辞書型データが返された場合、JSONに変換されます。

# 辞書型をJSONに変換するコード例
@app.route("/me")
def me_api():
    user = get_current_user()
    
    # 自動でJSONに変換
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }

APIのデザインによっては、辞書型とは違う型からJSONレスポンスを生成することも可能です。この場合jsonify()関数を使い、サポートされているJSONデータタイプをシリアル化します。また、より複雑なアプリケーションをサポートするFlaskコミュニティの拡張機能を利用する方法もあります。

# jsonify()を使う場合
@app.route("/users")
def users_api():
    users = get_all_users()
    return jsonify([user.to_json() for user in users])

10. セッション

リクエストオブジェクトに加えて、Sessionと呼ばれるセカンドオブジェクトが存在します。これはリクエストごとにユーザ固有のデータを保存することを可能とし、クッキー上に実装され、暗号学的な署名が施されます。これにより、ユーザはクッキーを参照できつつも、署名のために用いられたシークレットキーを知らない限り修正することができず、改ざんされにくくなります。このSessionを使うためにユーザは、シークレットキーを設定する必要があります。

<良質なシークレットキーを生成する方法>
シークレットキーはできる限り、ランダムであることが推奨されます。オペレーティングマシンには暗号学的にランダムな値を生成できる機能があり、次のコマンドはランダムな値を生成するものです。 $ python -c 'import os; print(os.urandom(16))'

クッキーをベースとしたSessionに関する注記: FlaskはSessionの値を取り出し、それをクッキーに格納します。リクエストを超えて値が保持されていない場合、クッキーは確かに有効です。そのため明確なエラーメッセージが表示されない場合でも、ページレスポンスのクッキーサイズとウェブブラウザでのサイズを比較することが推奨されます。

デフォルトのクライアントベースのセッションの他にサーバベースのセッションを取り扱いたい場合、拡張機能を利用することが推奨されています。

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

app = Flask(__name__)

#secret_keyの設定(通常は設定ファイルに格納する)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    #sessionの中にusernameを格納しているか確認
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

# *このコード例ではパスワードを照合する機能はここではつけていない
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # usernameがPOSTされたらsessionに格納する
        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からusernameを削除
    session.pop('username', None)
    return redirect(url_for('index'))
quickstart11
quickstart12
quickstart13
quickstart14

11. メッセージフラッシング

優れたアプリケーションやユーザインターフェイスは全てフィードバックに起因するものといえます。もしユーザがアプリケーションから十分なフィードバックを得られない場合、ユーザは結果的にそのアプリケーションを利用しなくなるでしょう。Flaskは、ユーザにフィードバックを返すとてもシンプルな機能を実装しており、これはフラッシングシステムを利用しています。このフラッシングシステムは基本的に、リクエストの最後にメッセージを記録し、次のリクエストでそのメッセージにアクセスして、利用できるようにするものです。たいてい、メッセージを返すためのレイアウトテンプレートと一緒に使用されます。

メッセージをフラッシュさせるためにはflash()メソッドを使います。メッセージを保持するためにはテンプレートで利用できるget_flashed_messages()が利用されます(参照:Message Flashing)。

12. ロギング

適切であるはずの処理データは、時に誤っていることもあります。例えば、HTTPリクエストをサーバに送る、クライアントサイドのコードにバグがある場合などです。これは、ユーザがデータを改変したり、クライアント側のコードにバグが含まれている場合などに起こります。多くの場合、400 Bad Requestエラーを返信できれば十分ですが、必ずしもそうならずコードが動き続けることもあります。

このような場合、コードの実行内容などは記録されるべきであり、これがログ機能が利用される理由です。Flaskは0.3ver以来、ログ機能は実装されています。このログ機能はPython公式のログ機能を利用しています(参照: logging, Application Errors)。

# コード例
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

13. WSGIミドルウェアの利用

WSGIミドルウェアをアプリケーションで利用したい場合、WSGIアプリケーションをラップすることで利用できます。例えば、Werkzeugパッケージからlighttpdで機能するミドルウェアを利用する場合、次のコードのように記述します。

# WSGIの中でバグを扱う場合
from werkzeug.contrib.fixers import LighttpdCGIRootFix

# WSGIアプリケーションをラップ(覆って)している
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)

14. Flask拡張機能を使う

Flask拡張機能は、汎用的なタスクの実行を助けるパッケージです。例えば、SQL-AlchemyはSQL-AlchemyをFlaskでシンプルかつ簡単に実行できるようにする拡張機能です。拡張機能については、Extensionsを参照下さい。

15. ウェブサーバへのデプロイ

Flaskではアプリケーションのデプロイ方法は、サーバの種類によって色々あります。デプロイについてはDeployment Optionを参照下さい。

最後に

以上がFlask公式ドキュメントのQuickstartです。基本的には原文に沿っていますが、適宜意訳や捕捉説明を入れています。Flaskは今人気が上がっており、サイトでも多くの情報が見つかりますが、バージョンが異なっていたり、専門用語が多くて意味がよくわからなかったりするものもあります。今回の4つの記事については現時点で最新バージョンのFlaskの基本的な使い方を押さえられるようにすることを念頭に置いており、参考になりましたら幸いです。読んで頂き、ありがとうございました。