HOWTO · Flask
Flask で url_for を使用して動的 URL を作成する
Flask の url_for() でエンドポイントから URL を生成し、動的なルート値を指定する方法を解説します。生成した URL を使ったリダイレクトや、Jinja テンプレートでのリンク作成も紹介します。
このページの内容
Flask では、url_for() にエンドポイント(通常はビュー関数名)を渡すと、その URL が生成されます。動的なパスを埋めるには、ルート値をキーワード引数で渡します。この関数が返すのは URL 文字列です。ブラウザーをその URL に移動させるには、ビューから redirect() のレスポンスを返します。
url_for() が URL を生成する仕組み
Flask は各ビューをエンドポイントに登録します。既定ではエンドポイント名にビュー関数名が使われるため、url_for("index") は index という名前のビューを指します。Flask はそのエンドポイントに対応するルートを参照して URL を組み立てます。そのため、アプリケーション内のあちこちにパス文字列を重複して書かずに、リンクをルート定義に結び付けられます。
生成されるのは / や /users/Harry のようなパスです。ルートに可変部分がある場合は、対応する名前のキーワード引数で値を渡します。ルートに含まれない追加の値は、クエリパラメーターになります。URL の生成だけでは、リクエストは送信されず、訪問者もリダイレクトされません。
ビュー関数から URL を生成する
次の小さなアプリには、ホーム、プロフィール、リダイレクトを行う各ビューと、Jinja のページが含まれています。テストクライアントはサーバーを起動せずに Jinja のページへリクエストし、リダイレクトのレスポンスを確認します。実行する前に、Python 環境へ Flask をインストールしてください。提示された実行環境では、テストクライアントがリンクをレンダリングし、Location ヘッダーが /users/Harry の 302 レスポンスを受け取りました。
次の例を実行します。
from flask import Flask, redirect, render_template_string, url_for
app = Flask(__name__)
@app.get("/")
def index():
return "Home"
@app.get("/users/<string:user_name>")
def profile(user_name):
return f"Profile: {user_name}"
@app.get("/go")
def go():
return redirect(url_for("profile", user_name="Harry"))
@app.get("/links")
def links():
return render_template_string(
'<a href="{{ url_for(\'profile\', user_name=\'Josh\') }}">Profile</a>'
)
with app.test_client() as client:
page = client.get("/links")
print(page.get_data(as_text=True))
response = client.get("/go", follow_redirects=False)
print(response.status_code)
print(response.headers["Location"])
<a href="/users/Josh">Profile</a>
302
/users/Harry
エンドポイントの引数は、ビュー関数名である profile です。Flask が /go を処理すると、url_for() がプロフィールのパスを生成し、redirect() がそのパスへのリダイレクトレスポンスを返します。テストクライアントはレンダリングされたリンクを出力した後、リダイレクトのステータスと Location ヘッダーを出力します。同じアプリで url_for("index") を使えば、ホームのパスを生成できます。
動的なルート値を渡す
ルート /users/<string:user_name> では、user_name という変数を宣言しています。url_for("profile", user_name="Harry") は、同じ変数に値を渡す呼び出しです。Flask はその値をパスに組み込み、必要に応じて URL エンコードします。URL の生成時にルートを照合できるよう、エンドポイント名と各ルート変数名をビューおよびキーワード引数に合わせてください。
たとえば、ルートが /articles/<int:article_id> の場合は、url_for("article", article_id=42) のように整数を渡します。ルート変数に使われなかった sort="recent" などの値は、クエリパラメーターになります。パスの構成にはルート変数を使い、クエリデータには追加のキーワード引数を使います。
生成した URL でリダイレクトする
ビュー内で redirect(url_for("profile", user_name="Harry")) を返します。url_for() が遷移先のパスを用意し、redirect() がブラウザーにそのパスへの移動を指示する HTTP レスポンスを作成します。例の /go ビューもこの方法を使っています。url_for(...) だけを返すと、URL 文字列がレスポンス本文として送られるだけで、ブラウザーは移動しません。
ルートが変わった場合も、リダイレクト先はエンドポイントに登録されたルートから決まるため、ビューにパスを直接書く必要はありません。リダイレクトは現在のリクエストへのレスポンスであり、移動先にはブラウザーが別途リクエストを送ります。この点を踏まえて、リダイレクトを使うか判断してください。
Jinja テンプレートでリンクを生成する
Jinja テンプレートでは、アンカーの href に同じエンドポイントとルート値を指定します。
<a href="{{ url_for('profile', user_name='Josh') }}">Profile</a>
ここでは、profile がビュー名に対応し、user_name が必須のルート部分を埋めています。実行可能な例では、このテンプレート文字列を /links でレンダリングするため、生成されたリンクはプロフィールのルートを指します。リンクに表示される「Profile」は、例を変更せず英語のままにしています。テンプレートをファイルに保存する場合は、Flask の render_template("index.html") で返し、そのファイルをアプリケーションの templates ディレクトリに置いてください。宛先のエンドポイントが宣言していないルート値は渡さず、アプリケーションで使われているエンドポイント名を正確に指定します。