【Flask】クイックスタート(第3回)

Flask

PythonのWEBフレームワークの一つであるFlaskの公式ドキュメントに沿ってクイックスタートを実践していきます。

Quickstart — Flask Documentation (3.1.x)

前回の記事の続きです。

リダイレクトとエラー

ユーザーを別のエンドポイントにリダイレクトするには、redirect() 関数を使用します。エラー コードを表示してリクエストを早期に中止するには、abort() 関数を使用します。

▼「hello.py」

from flask import Flask
from flask import abort, redirect, url_for

app = Flask(__name__)

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

http://127.0.0.1:5000/ にアクセス

http://127.0.0.1:5000/login にリダイレクトされ、

※ 401 Unauthorized のエラーが表示される。

※ 「this_is_never_excuted()」メソッドは実行されない。

※ ので、未定義でもエラーは発生しない。

これは、ユーザーがインデックスからアクセスできないページにリダイレクトされるため、あまり意味のない例ですが (401 はアクセス拒否を意味します)、それがどのように機能するかを示しています。

デフォルトでは、エラー コードごとに白黒のエラー ページが表示されます。エラー ページをカスタマイズしたい場合は、errorhandler() デコレータを使用できます。

▼「hello.py」

from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

▼「templates/page_not_found.html」の例

<!doctype html>
<title>404 Not Found | Hello from Flask</title>
Oops!
The page you're looking for is not found, sorry.

http://127.0.0.1:5000/ にアクセス

render_template() 呼び出しの後の 404 に注目してください。これは、そのページのステータス コードが見つからないことを意味する 404 であるべきであることを Flask に伝えます。デフォルトでは 200 が想定されており、これは「すべてうまくいきました」を意味します。

レスポンス

ビュー関数からの戻り値は、自動的に応答オブジェクトに変換されます。戻り値が文字列の場合、応答本文としての文字列、200 OK ステータス コード、および text/html MIME タイプを含む応答オブジェクトに変換されます。戻り値が辞書またはリストの場合、 jsonify() が呼び出されて応答が生成されます。 Flask が戻り値を応答オブジェクトに変換するために適用するロジックは次のとおりです。

1.正しいタイプの応答オブジェクトが返された場合、それはビューから直接返されます。

2.文字列の場合、そのデータとデフォルトのパラメーターを使用して応答オブジェクトが作成されます。

3.文字列またはバイトを返すイテレータまたはジェネレータの場合、ストリーミング応答として扱われます。

4.辞書またはリストの場合、応答オブジェクトは jsonify() を使用して作成されます。

5.タプルが返された場合、タプル内の項目によって追加情報が提供されることがあります。このようなタプルは、(応答、ステータス)、(応答、ヘッダー)、または (応答、ステータス、ヘッダー) の形式である必要があります。ステータス値はステータス コードをオーバーライドし、ヘッダーは追加のヘッダー値のリストまたは辞書にすることができます。

6.いずれも機能しない場合、Flask は戻り値が有効な WSGI アプリケーションであると想定し、それを応答オブジェクトに変換します。

結果の応答オブジェクトをビュー内で取得したい場合は、make_response() 関数を使用できます。

次のようなビューがあると想像してください。

from flask import Flask
from flask import render_template

app = Flask(__name__)

@app.errorhandler(404)
def not_found(error):
    return render_template('page_not_found.html'), 404

make_response() で return 式をラップし、応答オブジェクトを取得して変更してから返すだけです。

from flask import Flask
from flask import render_template
from flask import make_response

app = Flask(__name__)

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('page_not_found.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

http://127.0.0.1:5000/ にアクセスした際のレスポンスヘッダー

JSONを使用したAPI

API を作成するときの一般的な応答形式は JSON です。 Flask を使用してこのような API を書き始めるのは簡単です。ビューから辞​​書またはリストを返す場合、それは JSON 応答に変換されます。

▼「hello.py」

from flask import Flask
from flask import url_for

app = Flask(__name__)

@app.route("/user/image")
def user_image():
    return ""

@app.route("/api/me")
def me_api():
    user = get_current_user()
    print(user.name)
    return {
        "username": user.name,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }

@app.route("/api/users")
def users_api():
    users = get_all_users()
    return [user.to_dict() for user in users]
    
def get_current_user():
    return User("hoge", "default", "hoge.png")

def get_all_users():
    return [
        User("hoge", "default", "hoge.png"),
        User("fuga", "light", "fuga.png"),
        User("piyo", "dark", "piyo.png"),
    ]

class User:
    def __init__(self, name, theme="default", image="nobody.png"):
        self.name=name
        self.theme=theme
        self.image=image

    def to_dict(self):
        return {"name": self.name, "theme": self.theme, "image": self.image}

http://127.0.0.1:5000/api/me にアクセスした場合

http://127.0.0.1:5000/api/users にアクセスした場合

これは、サポートされている JSON データ型をシリアル化する jsonify() 関数にデータを渡すためのショートカットです。つまり、辞書またはリスト内のすべてのデータは JSON シリアル化可能である必要があります。

データベース モデルなどの複雑な型の場合は、最初にシリアル化ライブラリを使用してデータを有効な JSON 型に変換する必要があります。より複雑なアプリケーションをサポートする、コミュニティによって維持されているシリアル化ライブラリと Flask API 拡張機能が多数あります。

セッション

request オブジェクトに加えて、session と呼ばれる 2 番目のオブジェクトもあります。これを使用すると、あるリクエストから次のリクエストまでユーザー固有の情報を保存できます。これは Cookie の上に実装され、Cookie に暗号的に署名されます。これが意味するのは、署名に使用された秘密鍵を知らない限り、ユーザーは Cookie の内容を見ることはできても、変更することはできないということです。

セッションを使用するには、秘密キーを設定する必要があります。セッションの仕組みは次のとおりです。

▼「hello.py」

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

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {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():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

http://127.0.0.1:5000/ にアクセスした場合

http://127.0.0.1:5000/login にアクセスした場合

※ログイン名を入力して「Login」ボタンを押下する

▼「Login」ボタン押下後

※トップにリダイレクトされ、ログイン名が表示される。

http://127.0.0.1:5000/logout にアクセスした場合

※セッションの「username」が削除されトップへリダイレクトされる。

秘密キーはできるだけランダムである必要があります。オペレーティング システムには、暗号化ランダム ジェネレーターに基づいてかなりランダムなデータを生成する方法があります。次のコマンドを使用して、Flask.secret_key (または SECRET_KEY) の値をすばやく生成します。

python -c 'import secrets; print(secrets.token_hex())'

Cookie ベースのセッションに関する注意: Flask はセッション オブジェクトに入力した値を受け取り、それらを Cookie にシリアル化します。一部の値がリクエスト間で保持されず、Cookie が実際に有効になっており、明確なエラー メッセージが表示されない場合は、ページ応答内の Cookie のサイズを Web ブラウザでサポートされているサイズと比較して確認してください。

デフォルトのクライアント側ベースのセッションに加えて、代わりにサーバー側でセッションを処理したい場合は、これをサポートする Flask 拡張機能がいくつかあります。

メッセージのフラッシュ

優れたアプリケーションとユーザー インターフェイスはフィードバックがすべてです。ユーザーが十分なフィードバックを得られないと、おそらくアプリケーションを嫌いになってしまうでしょう。 Flask は、フラッシュシステムを使用してユーザーにフィードバックを提供する非常に簡単な方法を提供します。フラッシュ システムは基本的に、リクエストの最後にメッセージを記録し、次の (そしてその次の) リクエストでそれにアクセスすることを可能にします。通常、これはレイアウト テンプレートと組み合わせてメッセージを公開します。

メッセージをフラッシュするには flash() メソッドを使用し、メッセージを取得するには get_flashed_messages() を使用します。これもテンプレートで使用できます。完全な例については、「メッセージのフラッシュ」を参照してください。

ロギング

正しいはずのデータを扱っているのに、実際には正しくないという状況に陥ることがあります。たとえば、HTTP リクエストをサーバーに送信するクライアント側のコードがあるとしますが、それは明らかに形式が間違っています。これは、ユーザーによるデータの改ざん、またはクライアント コードの失敗が原因である可能性があります。ほとんどの場合、そのような状況では 400 Bad Request で応答しても問題ありませんが、場合によってはそれができず、コードが動作し続ける必要があります。

何か怪しいことが起こったことを記録しておきたいかもしれません。ここでロガーが役に立ちます。 Flask 0.3 では、ロガーが使用できるように事前設定されています。

ログ呼び出しの例をいくつか示します。

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

▼「hello.py」の実装例

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

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {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']
        app.logger.warning('User %s logged in.', session['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():
    # remove the username from the session if it's there
    app.logger.warning('User %s logged out.', session['username'])
    session.pop('username', None)
    return redirect(url_for('index'))

▼ビルトインサーバーのターミナルに出力される

WSGIミドルウェアでのフック

WSGI ミドルウェアを Flask アプリケーションに追加するには、アプリケーションの wsgi_app 属性をラップします。たとえば、Nginx の背後で実行するために Werkzeug の ProxyFix ミドルウェアを適用するには、次のようにします。

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)

app の代わりに app.wsgi_app をラップすると、アプリはミドルウェアではなく Flask アプリケーションを引き続き参照するため、アプリの使用と構成を直接続行できます。

Flask拡張機能の使用

拡張機能は、一般的なタスクの実行に役立つパッケージです。たとえば、Flask-SQLAlchemy は、Flask での使用をシンプルかつ簡単にする SQLAlchemy サポートを提供します。

Flask 拡張機能の詳細については、「拡張機能」を参照してください。

以上です。

  • 0
  • 0
  • 0
  • 0

コメント

タイトルとURLをコピーしました