こんにちは、かつコーチです。
これまでの記事では、return "文字列"で画面を表示してきましたが、実際のWebアプリでは装飾されたHTMLを表示したいはずです。
この記事では、Flaskに標準搭載されているJinja2というテンプレートエンジンの使い方を解説します。
Jinja2とは?
HTMLに変数やロジックを埋め込む仕組み
Jinja2(ジンジャツー)とは、HTMLファイルの中にPythonの変数や条件分岐を埋め込めるようにするテンプレートエンジン(雛形に動的なデータを流し込んで最終的なHTMLを作る仕組み)です。
Flaskには標準でJinja2が組み込まれているため、追加のインストールは不要です。
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html", username="かつコーチ")
render_template関数がJinja2の呼び出し口で、第1引数にテンプレートファイル名、それ以降にテンプレートへ渡したい変数を指定します。
templatesフォルダの役割
Flaskは、プロジェクト直下のtemplatesという名前のフォルダを自動的に探しに行きます。
flask-first-app/
├── app.py
└── templates/
└── index.html
フォルダ名をtemplatesから変更するには追加の設定が必要なので、まずはこの標準構成に慣れておくのがおすすめです。
基本の書き方・実装手順
手順1: 変数をHTMLに埋め込む
templates/index.htmlを作成し、{{ }}(二重波括弧)で変数を埋め込みます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ホーム</title>
</head>
<body>
<h1>こんにちは、{{ username }}さん!</h1>
</body>
</html>
render_templateで渡したusername="かつコーチ"が、{{ username }}の部分に展開されます。
手順2: 条件分岐と繰り返しを使う
Jinja2では{% %}(波括弧とパーセント)を使って、ifやforといった制御構文を書けます。
@app.route("/dashboard")
def dashboard():
items = ["タスク1", "タスク2", "タスク3"]
is_admin = True
return render_template("dashboard.html", items=items, is_admin=is_admin)
<body>
{% if is_admin %}
<p>管理者としてログイン中です</p>
{% else %}
<p>一般ユーザーとしてログイン中です</p>
{% endif %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
{% if %}や{% for %}の終わりには、必ず{% endif %}や{% endfor %}が必要になる点がPythonの書き方と異なります。
手順3: テンプレートの共通部分を継承する
すべてのページでヘッダーやフッターを毎回書くのは非効率です。
Jinja2のテンプレート継承を使うと、共通レイアウトを1つのファイルにまとめられます。
templates/base.html(親テンプレート):
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{% block title %}マイサイト{% endblock %}</title>
</head>
<body>
<header><h1>マイサイト</h1></header>
<main>
{% block content %}{% endblock %}
</main>
<footer>© 2026 かつコーチ</footer>
</body>
</html>
templates/index.html(子テンプレート):
{% extends "base.html" %}
{% block title %}ホーム | マイサイト{% endblock %}
{% block content %}
<p>ようこそ、{{ username }}さん!</p>
{% endblock %}
{% extends %}で親テンプレートを継承し、{% block %}で囲んだ部分だけを子テンプレート側で上書きします。
ページが増えるほど、この仕組みの恩恵は大きくなります。
つまずきやすい設定・注意点
| safeフィルタは慎重に使う
Jinja2はデフォルトで、変数に含まれるHTMLタグを自動的に無害化する自動エスケープ(<script>などのタグをそのまま表示用の文字列に変換し、意図しないコードとして実行されないようにする仕組み)が有効になっています。
<p>{{ user_comment }}</p>
もしuser_commentに<script>alert('攻撃')</script>のような文字列が入っていても、Jinja2は自動的に無害な文字列として表示してくれます。
これを{{ user_comment | safe }}のようにsafeフィルタで解除すると、自動エスケープが無効になります。
自分で完全に信頼できるデータ以外にsafeを使うと、XSS(クロスサイトスクリプティング)という脆弱性の原因になるため、基本的には使わない方針を徹底しましょう。
よくあるつまずきポイント・エラー対処
jinja2.exceptions.TemplateNotFound
❌ Before:templatesフォルダを作らずにrender_template("index.html")を呼び出し、jinja2.exceptions.TemplateNotFound: index.htmlというエラーが出る
✅ After:app.pyと同じ階層にtemplatesフォルダを作成し、その中にHTMLファイルを配置する
私が最初につまずいたのはまさにこのエラーでした。
templatesという名前を1文字でも間違える(Templateと大文字にする、templateと単数形にするなど)とFlaskはフォルダを見つけられません。
エラーメッセージにファイル名だけが表示されるため、最初はどこが間違っているのか分かりにくいポイントです。
変数名の渡し忘れでUndefinedErrorが起きる
❌ Before:テンプレート側で{{ username }}を使っているのに、render_template("index.html")でusernameを渡し忘れる
この場合エラーにはならず、単に空文字として表示されてしまうことが多く、気づきにくいバグになります。
✅ After:render_templateに渡す変数名と、テンプレート内で使う変数名が一致しているかを、実装のたびに見比べる習慣をつける
より厳格にしたい場合は、Jinja2の設定で未定義変数をエラーにするStrictUndefinedを使う方法もあります。
応用・一歩先の使い方
static フォルダでCSS・画像を管理する
HTMLを装飾するCSSファイルは、templatesではなくstaticという名前のフォルダに配置します。
flask-first-app/
├── app.py
├── templates/
│ └── index.html
└── static/
└── style.css
HTMLからはurl_forを使って参照します。
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
url_forを使うことで、staticフォルダのパスが変わってもHTML側の修正が不要になります。
まとめ
この記事のポイント
- Jinja2はFlask標準搭載のテンプレートエンジン
{{ }}で変数展開、{% %}でif・forなどの制御構文を書く{% extends %}と{% block %}でレイアウトを共通化できる- 自動エスケープはXSS対策として重要なので
safeフィルタは慎重に使う templatesフォルダの命名ミスがエラーの定番原因
次に読むべき記事
画面表示ができるようになったら、次はデータをどう保存するかがテーマです。
「Flask-SQLAlchemyでモデルを定義しCRUD操作をする」で、データベースとの連携方法を解説します。
タグ: Flask, 初心者向け, フレームワーク基礎