概要(テンプレート=「HTML の型にデータを流し込む仕組み」)
テンプレート(Jinja2)は一言でいうと、
「HTML のひな型(型)をあらかじめ用意しておいて、
Python のデータをそこに流し込んで、“完成した HTML” を作る仕組み」
です。
Python で Web アプリを作るとき、
HTML を全部 """...""" の文字列でベタ書きする
→ すぐに地獄になります。
テンプレートを使うと、
見た目(HTML, CSS)はテンプレートファイルにまとめる
中身のデータ(ユーザー名、一覧、メッセージなど)は Python から渡す
という役割分担ができて、コードが一気に整理されます。
FastAPI では内部的に Jinja2 を使ったテンプレート機能が用意されていて、
とても少ないコードで「HTML を返すページ」を作れます。
HTML を Python だけで作ろうとすると何がつらいか
f文字列や文字列連結だけで HTML を生成する世界
まず、テンプレートを使わないケースを想像してみます。
たとえば、ユーザー名を埋め込んだ簡単なページを作りたい。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
def index():
username = "Taro"
html = f"""
<html>
<head><title>Welcome</title></head>
<body>
<h1>こんにちは、{username} さん</h1>
</body>
</html>
"""
return html
Python最初は「これでも全然いけるじゃん」と感じます。
しかし、少しずつ機能が増えると、すぐにこうなります。
ヘッダやフッタを複数ページで使い回したい
「ログインしているとき」と「していないとき」で表示を変えたい
一覧表示や条件分岐が増えて HTML がどんどん長くなる
全てを Python の文字列として扱うと、
見た目の修正のたびに Python のファイルをいじることになる
デザイナーやフロント担当と役割分担しにくい
HTML の中に Python が、Python の中に HTML が入り込んでカオスになる
という状況に陥ります。
ここで登場するのが「テンプレート」です。
テンプレート(Jinja2)の基本イメージ
「HTML の中に、穴を開けておく」感覚
テンプレートでは、まず「HTML のひな型ファイル」を作ります。
そこに、あとで Python から埋めたい場所を「変数」や「制御構文」として書いておきます。
例えば、templates/index.html というファイルを用意します。
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>こんにちは、{{ username }} さん</h1>
</body>
</html>
この {{ username }} の部分が「穴」です。
ここに Python 側から渡した username の値が差し込まれます。
Jinja2 では、
{{ ... }} は「値を埋め込む」{% ... %} は「制御(if, for など)」
というルールだけまず覚えておけば OK です。
Python 側では、このテンプレートにデータを渡して「レンダリング」します。
FastAPI と Jinja2 テンプレートの基本的な使い方
準備:テンプレートフォルダと Jinja2Templates
まず、ディレクトリ構成の例を決めます。
project
├ main.py
└ templates
└ index.html
templates/index.html はさっきの HTML だとします。
main.py でテンプレート機能を使えるようにします。
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
Pythondirectory="templates" で、テンプレートファイルが入っているフォルダを指定しています。
エンドポイントでテンプレートをレンダリングして返す
次に、トップページ用のエンドポイントを作ります。
from fastapi.responses import HTMLResponse
@app.get("/", response_class=HTMLResponse)
def index(request: Request):
username = "Taro"
return templates.TemplateResponse(
"index.html",
{"request": request, "username": username},
)
Pythonここで重要なポイントを整理します。
一つ目。response_class=HTMLResponse と書くことで、「HTML を返すエンドポイントですよ」と宣言しています。
これにより、ブラウザで開くとちゃんと HTML ページとして表示されます。
二つ目。TemplateResponse("index.html", {...}) で、テンプレートファイル名と「テンプレートに渡す値の辞書」を指定しています。
三つ目。コンテキスト(第2引数の dict)に "request": request を必ず含める必要があることです。
FastAPI のテンプレート統合では、request オブジェクトをテンプレートに渡す決まりになっています。
ここは「お作法」として覚えておいてください。
このコードでサーバーを起動して http://127.0.0.1:8000/ にアクセスすると、{{ username }} が Taro に置き換わった HTML が表示されます。
Jinja2 の基本文法(変数展開・for・if)
Jinja2 は「HTML の中でちょっとしたロジックを書くためのミニ言語」です。
ここでは、初心者がまず使う代表的なものに絞って説明します。
変数展開:{{ … }} で値を埋め込む
最初に出てきた {{ username }} が、変数展開です。
Python 側で
return templates.TemplateResponse(
"index.html",
{"request": request, "username": "Taro"},
)
Pythonと渡した場合、
テンプレート内の {{ username }} が "Taro" に置き換わります。
{{ }} の中では、ドットで属性にアクセスできます。
例えば、ユーザーモデル user.name など。
<p>ユーザー名: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
Python 側で {"user": user_obj} を渡しておけば、
テンプレートから user.name や user.email が参照できます。
繰り返し:{% for … %} ~ {% endfor %}
一覧を表示したいときは、for 文を使います。
例えば、アイテムのリストを表示するとします。
Python 側:
items = [
{"name": "Apple", "price": 100},
{"name": "Banana", "price": 80},
]
return templates.TemplateResponse(
"items.html",
{"request": request, "items": items},
)
Pythonテンプレート items.html:
<!DOCTYPE html>
<html>
<body>
<h1>商品一覧</h1>
<ul>
{% for item in items %}
<li>{{ item.name }}: {{ item.price }} 円</li>
{% endfor %}
</ul>
</body>
</html>
ここがポイントです。
{% for item in items %} で繰り返し開始{% endfor %} で繰り返し終了
中では {{ item.name }} のように変数展開
Python の for 文に似ていますが、「{% %}」で囲むことを忘れないでください。
条件分岐:{% if … %} ~ {% endif %}
表示を条件によって変えたいときは、if を使います。
例えば、「ログインしているときだけ名前を出したい」場合。
Python 側:
user = {"name": "Taro"} # ログインしていないときは None にする、など
return templates.TemplateResponse(
"index.html",
{"request": request, "user": user},
)
Pythonテンプレート:
<body>
{% if user %}
<p>こんにちは、{{ user.name }} さん</p>
{% else %}
<p>こんにちは、ゲストさん</p>
{% endif %}
</body>
{% if user %}{% else %}{% endif %}
Python にかなり近い書き味です。
こういう「ちょっとした条件分岐」を、
Python ではなくテンプレート側に寄せてあげると、
見た目の制御がすごく書きやすくなります。
テンプレートの分割と継承(ヘッダ・フッタの共通化)
テンプレートの強みは、「共通部分をまとめて再利用できる」ことです。
特に、全ページ共通のヘッダやフッタを持たせるときに威力を発揮します。
ここでは基本的なテンプレート継承のイメージだけ掴みます。
ベーステンプレートを作る(base.html)
共通部分(HTML の骨組み)を base.html に切り出します。
templates/base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>
<h1>My Site</h1>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2025 My Site</p>
</footer>
</body>
</html>
ここで {% block title %} や {% block content %} が
「子テンプレートが上書きする部分」です。
ページごとのテンプレートで base.html を継承する
トップページ用のテンプレートを作ります。
templates/index.html:
{% extends "base.html" %}
{% block title %}ホーム{% endblock %}
{% block content %}
<p>ようこそ、トップページへ。</p>
{% endblock %}
{% extends "base.html" %} と書くことで、
「このテンプレートは base.html を土台として使います」と宣言します。
そして、base.html で定義した block を{% block ... %}{% endblock %} で上書きしています。
結果として、
HTML 頭からフッタまでは base.html
中身の <main> 部分や <title> だけ index.html で差し替え
という構造になります。
この仕組みを使うと、
ヘッダやメニュー、フッタを一箇所で管理
ページごとの中身だけ別テンプレートに分ける
という、きれいな構造にできます。
テンプレートを使うときの設計の考え方(重要ポイント)
「ロジックは Python、見た目はテンプレート」に分ける
テンプレートを使うときの基本方針は、
ビジネスロジックや計算・DB 取得は Python 側でやる
テンプレート側には「表示ロジック」だけを書く
という分担です。
例えば、
DB からユーザー一覧を取る
フィルタリングやソートをする
といった処理は Python の関数内でやり切り、
テンプレートには「渡された users を for で回して表示する」だけを書きます。
テンプレート内にあまり複雑なロジック(ネストしまくった if、計算式など)を書き始めると、
今度はテンプレートが読みにくくなります。
「テンプレートは“HTML の型”と“簡単な条件・繰り返し”のための場所」と意識しておくのが大事です。
「データの整形をどこでするか」を意識する
例えば「価格を 3 桁区切りで表示したい」といったとき、
Python 側で "{:,}".format(price) してからテンプレートに渡すか、
テンプレート側でフィルタを使って整形するか、という選択肢があります。
初心者のうちは、
テンプレートには「ほぼ生の値」を渡し、
整形は Python 側で済ませておく
方が理解しやすいことが多いです。
慣れてきたら、Jinja2 のフィルタやカスタムフィルタを使う方向に進むとよいです。
まとめ(テンプレートは「HTML と Python をいい感じに分離するための道具」)
Python の Web フレームワーク(FastAPI)におけるテンプレート(Jinja2)を整理すると、こうなります。
テンプレートは、HTML のひな型に「穴」を開けておき、そこに Python から渡したデータを流し込んで最終的な HTML を作る仕組み。
FastAPI では Jinja2Templates(directory="templates") と TemplateResponse("file.html", {"request": request, ...}) を使うことで、テンプレートから HTML を返すエンドポイントを簡単に作れる。
テンプレート内では {{ 変数 }} で値を埋め込み、{% for ... %} や {% if ... %} で「一覧表示」や「条件表示」を表現する。これは「見た目寄りのロジック」としてテンプレート側に任せる。
base.html のようなベーステンプレートを作り、{% extends %} と {% block %} を使うことで、ヘッダやフッタなどの共通部分とページごとの中身をきれいに分離できる。
設計のポイントは、「ビジネスロジックやデータ取得は Python、表示・レイアウトや簡単な条件分岐はテンプレート」と役割を分けること。これによってコードが読みやすく、保守しやすくなる。
