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

Flask

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

Quickstart — Flask Documentation (3.1.x)

前回の記事の続きです。

HTTPメソッド

Web アプリケーションは、URL にアクセスするときにさまざまな HTTP メソッドを使用します。 Flask を使用するときは、HTTP メソッドについてよく理解しておく必要があります。デフォルトでは、ルートは GET リクエストにのみ応答します。 route() デコレーターのメソッド引数を使用して、さまざまな HTTP メソッドを処理できます。

▼同一関数内でメソッドにより処理切替する例

from flask import request

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

上記の例では、ルートのすべてのメソッドを 1 つの関数内に保持しています。これは、各部分が共通のデータを使用する場合に便利です。

さまざまなメソッドのビューをさまざまな関数に分離することもできます。 Flask は、一般的な HTTP メソッドごとに get()post() などでそのようなルートを装飾するためのショートカットを提供します。

▼メソッド毎に別関数で処理する例

@app.get('/login')
def login_get():
    return show_the_login_form()

@app.post('/login')
def login_post():
    return do_the_login()

GET が存在する場合、Flask は自動的に HEAD メソッドのサポートを追加し、HTTP RFC に従って HEAD リクエストを処理します。同様に、OPTIONS も自動的に実装されます。

▼「hello.py」の実装例:「/login」へのアクセス

※GETメソッド:ログインフォームを表示する

※POSTメソッド:ログイン処理を実行する

from flask import Flask
from flask import url_for
from flask import request

app = Flask(__name__)

@app.route('/login', methods=['GET', 'POST'])
def login():
    # メソッドで処理切替
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

# ログイン処理
def do_the_login():
    return "logged in!"

# ログインフォーム表示
def show_the_login_form():
    return """
<form action='{target_url}' method='POST'>
<table>
<tr>
<td><label>email:</label></td>
<td><input type='text' name='email' placeholder='メールアドレスを入力'></td>
</tr>
<tr>
<td><label>password:</label></td>
<td><input type='password' name='password'></td>
</tr>
<tr>
<td><button type='submit'>ログイン</button></td>
</tr>
</table>
</form>
""".format(target_url=url_for('login'))

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

▼emailとpasswordを入力して「ログイン」をクリック

▼「/login」へPOSTアクセスした場合

静的ファイル

動的 Web アプリケーションには静的ファイルも必要です。通常、CSS ファイルと JavaScript ファイルはそこから取得されます。理想的には、Web サーバーがそれらを提供するように構成されていますが、開発中に Flask がそれを行うこともできます。パッケージ内またはモジュールの隣に static というフォルダーを作成するだけで、アプリケーションの /static で使用できるようになります。

静的ファイルの URL を生成するには、特別な「静的」エンドポイント名を使用します。

url_for('static', filename='style.css')

ファイルは、ファイルシステムに static/style.css として保存する必要があります。

▼静的ファイルの保存場所「myproject/static/」

▼「static/style.css」の例

form table {
    border: 1px #0000ff solid;
    padding: 8px;
}

▼前段での「/login」にGETアクセスした場合の例

TABLEタグのボーダーが適用されているのが判ります。

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

Python 内から HTML を生成するのは楽しいものではなく、アプリケーションの安全性を保つために自分で HTML エスケープを行う必要があるため、実際には非常に面倒です。そのため、Flask は Jinja2 テンプレート エンジンを自動的に設定します。

テンプレートを使用して、あらゆるタイプのテキスト ファイルを生成できます。 Web アプリケーションの場合、主に HTML ページを生成しますが、マークダウン、電子メール用のプレーン テキスト、その他何でも生成することもできます。

HTML、CSS、およびその他の Web API のリファレンスについては、MDN Web ドキュメントを使用してください。

テンプレートをレンダリングするには、render_template() メソッドを使用できます。必要なのは、テンプレートの名前と、キーワード引数としてテンプレート エンジンに渡したい変数を指定することだけです。テンプレートをレンダリングする方法の簡単な例を次に示します。

▼「hello.py」

from flask import Flask
from flask import render_template

app = Flask(__name__)

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

Flask は templates フォルダーでテンプレートを探します。したがって、アプリケーションがモジュールの場合、このフォルダーはそのモジュールの隣にあり、パッケージの場合、実際にはパッケージ内にあります。

▼ケース1:モジュール

/application.py
/templates
    /hello.html

※今回はこちらのケース

▼ケース2:パッケージ

/application
    /__init__.py
    /templates
        /hello.html

テンプレートについては、Jinja2 テンプレートの機能を最大限に活用できます。詳細については、Jinja2 テンプレートの公式ドキュメントを参照してください。

テンプレートの例を次に示します。

▼「templates/hello.html」

<!doctype html>
<title>Hello from Flask</title>
{% if person %}
  <h1>Hello {{ person }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

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

http://127.0.0.1:5000/hello/hoge%20fuga にアクセスした場合

テンプレート内では、config、request、session、g オブジェクト、および url_for() および get_flashed_messages() 関数にもアクセスできます。

テンプレートは、継承が使用されている場合に特に便利です。その仕組みを知りたい場合は、「テンプレートの継承」を参照してください。基本的に、テンプレートの継承により、各ページに特定の要素 (ヘッダー、ナビゲーション、フッターなど) を保持できるようになります。

自動エスケープが有効になっているため、person に HTML が含まれている場合は自動的にエスケープされます。変数を信頼でき、それが安全な HTML であることがわかっている場合 (たとえば、Wiki マークアップを HTML に変換するモジュールからのものであるため)、Markup クラスを使用するか、テンプレート内で|safe フィルターを使用して、その変数を安全としてマークできます。その他の例については、Jinja 2 のドキュメントを参照してください。

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

Web アプリケーションの場合、クライアントがサーバーに送信するデータに反応することが重要です。 Flask では、この情報はグローバルリクエストオブジェクトによって提供されます。

リクエスト オブジェクトについては API セクションに記載されており、ここでは詳しく説明しません (リクエストを参照)。ここでは、最も一般的な操作の概要をいくつか示します。まず最初に、flask モジュールからインポートする必要があります。

from flask import request

現在のリクエスト メソッドは、method 属性を使用して利用できます。フォーム データ (POST または PUT リクエストで送信されるデータ) にアクセスするには、form 属性を使用できます。上記の 2 つの属性の完全な例を次に示します。

▼「hello.py」

from flask import Flask
from flask import render_template
from flask import request
from markupsafe import escape

app = Flask(__name__)

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['email'],
                       request.form['password']):
            return log_the_user_in(request.form['email'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

def valid_login(email, password):
    if (email == 'hoge@example.com' and password == 'password'):
        return True
    return False

def log_the_user_in(email):
    return f"{escape(email)} successfully logged in!"

▼「templates/login.html」

<!doctype html>
<title>Hello from Flask</title>
{% if error %}
  <h2>Error:</h2>
  <p>{{ error }}</p>
{% endif %}
<form action="{{url_for('login')}}" method="POST">
    <table>
        <tr>
            <td><label>email:</label></td>
            <td><input type='text' name='email' placeholder='メールアドレスを入力'></td>
        </tr>
        <tr>
            <td><label>password:</label></td>
            <td><input type='password' name='password'></td>
        </tr>
        <tr>
            <td><button type='submit'>ログイン</button></td>
        </tr>
    </table>
</form>

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

▼そのまま「ログイン」ボタンを押下

エラーが表示される。

▼「hoge@example.com」「password」を入力して「ログイン」

▼ログイン成功

フォーム属性にキーが存在しない場合はどうなりますか?その場合、特別な KeyError が発生します。標準の KeyError と同じようにキャッチできますが、それを行わないと、代わりに HTTP 400 Bad Request エラー ページが表示されます。したがって、多くの状況では、その問題に対処する必要はありません。

URL (?key=value) で送信されたパラメータにアクセスするには、args 属性を使用できます。

searchword = request.args.get('key', '')

ファイルアップロード

Flask を使用すると、アップロードされたファイルを簡単に扱うことができます。 HTML フォームに enctype=’multipart/form-data’ 属性を設定することを忘れないように注意してください。設定しないと、ブラウザはファイルをまったく送信しません。

アップロードされたファイルは、メモリまたはファイル システム上の一時的な場所に保存されます。リクエスト オブジェクトの files 属性を確認することで、これらのファイルにアクセスできます。アップロードされた各ファイルはその辞書に保存されます。これは標準の Python ファイル オブジェクトと同じように動作しますが、そのファイルをサーバーのファイル システムに保存できる save() メソッドもあります。これがどのように機能するかを示す簡単な例を次に示します。

▼「hello.py」

from flask import Flask
from flask import request
from flask import url_for
import os

app = Flask(__name__)

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    message = ""
    if request.method == 'POST':
        f = request.files['the_file']
        f.save(os.getcwd() + '/uploads/uploaded_file.txt')
        message = "アップロードしたファイルを保存しました。"
    return """
{message}
<form action="{target_url}" method="POST" enctype="multipart/form-data">
<p><label>アップロードするファイル:</label><input type="file" name="the_file"></p>
<p><button type="submit">アップロード</button></p>
</form>
""".format(message=message, target_url=url_for('upload_file'))

▼「uploads」フォルダを作成

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

▼適当にファイルを選択して「アップロード」ボタン押下

▼アップロード完了

▼ファイルが保存されていることを確認

ファイルがアプリケーションにアップロードされる前にクライアントでどのように名前が付けられたかを知りたい場合は、filename 属性にアクセスできます。ただし、この値は偽造される可能性があるため、その値を決して信頼しないでください。クライアントのファイル名を使用してファイルをサーバーに保存する場合は、Werkzeug が提供する secure_filename() 関数にそのファイル名を渡します。

▼コード例

from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['the_file']
        file.save(f"/var/www/uploads/{secure_filename(file.filename)}"

クッキー

Cookie にアクセスするには、Cookie 属性を使用できます。 Cookie を設定するには、応答オブジェクトの set_cookie メソッドを使用できます。リクエスト オブジェクトの cookie 属性は、クライアントが送信するすべての cookie を含む辞書です。セッションを使用したい場合は、Cookie を直接使用せず、Cookie にセキュリティを追加する Flask のセッションを使用してください。

▼「hello.py」:クッキーの読込例

from flask import Flask
from flask import request

app = Flask(__name__)

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # クッキーが無い場合にKeyErrorが発生しないように、
    # cookies[key] の代わりに cookies.get(key) を使いましょう。

▼「hello.py」:クッキー読取&保存例

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

app = Flask(__name__)

@app.route('/')
def index():
    username = request.cookies.get('username')
    if (username is None):
        username = "New User"
    # クッキーが無い場合にKeyErrorが発生しないように、
    # cookies[key] の代わりに cookies.get(key) を使いましょう。
    resp = make_response(render_template('index.html', username=username))
    resp.set_cookie('username', username)
    return resp

▼「templates/index.html」の例

<!doctype html>
<title>Hello from Flask</title>
{% if username %}
  <h1>Hello {{ username }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

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

クッキーに「username」の値「New User」が保存されているのを確認。

Cookie は応答オブジェクトに設定されることに注意してください。通常はビュー関数から文字列を返すだけなので、Flask はそれらを応答オブジェクトに変換します。明示的にそれを行いたい場合は、make_response() 関数を使用してそれを変更できます。

場合によっては、応答オブジェクトがまだ存在しない時点で Cookie を設定したい場合があります。これは、遅延リクエスト コールバック パターンを利用することで可能になります。

今回は以上です。次回はこの続きを予定しています。

  • 0
  • 0
  • 0
  • 0

コメント

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