日曜日, 4月 26, 2026

Flask の Blueprint のテンプレート問題

 Flask の Blueprint は、ルート、静的ファイル、テンプレートをまとめて管理できます。しかし、テンプレートが指定できません。

ここでは、Blueprint の template_folder の問題点と回避策を説明します。


Blueprint のテンプレート問題

一般的な Blueprint の定義は次のようになります。

tags_bp = Blueprint(
"tags",
__name__,
url_prefix="/tags",
template_folder="../templates/tags"
)

この設定で期待されるのは

  • この Blueprint は templates/tags/ 内のテンプレートを使用する

  • render_template("list.html") を呼び出すと、templates/tags/list.html が読み込まれる

ところが Blueprint  ではテンプレートが指定できません。


どうなるかというと

Flask は Blueprint ごとにテンプレートを分離しません。 すべての Blueprint のテンプレートディレクトリを 一つの検索パスに統合します。

Flask のテンプレート検索は次のように行われます。

  1. すべての Blueprint の template_folder を収集

  2. グローバルな templates/ ディレクトリを追加

  3. それらを一つのリストにまとめる

  4. ファイル名だけで検索する

  5. 最初に見つかったファイルを採用する

このため、同じ名前のテンプレートがあると衝突します。

templates/tags/list.html
templates/words/list.html

Blueprint の登録順によっては /tags にアクセスしても words/list.html が読み込まれます。


回避策


1. 名前空間付きパスを使用する

Blueprint の template_folder を使わず、次のように明示します。

return render_template("tags/list.html")


2. テンプレート名をユニークにする

tags_list.html
words_list.html

Blueprint ごとにファイル名を変える方法です。 規模が大きくなると管理が難しくなります。


3. 独自の Jinja 環境を使用する(完全分離)

Blueprint ごとに独立したテンプレートローダーを作成します。

from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader([
"templates/tags",
"templates"
]))

この方法は Flask のテンプレートローダーを完全に回避できますが、 CSRF などの Flask コンテキストを手動で注入する必要があります。


結論

方法3でハマりました。これだと Flask の正規なルートを通らないのでエラーが出ます(CSRFトークンがないとか)。方法1が無難です。定数を設定して回避する古典的な手法がおすすめです。

Flask の Blueprint のテンプレート問題

  Flask の Blueprint は、ルート、静的ファイル、テンプレートをまとめて管理できます。しかし、テンプレートが指定できません。 ここでは、Blueprint の template_folder の問題点と回避策を説明します。 Blueprint のテンプレート問題...