【Django】テンプレートの基本とテンプレートタグの使い方

Django

こんにちは、かつコーチです。

前回はビュー(View)の書き方を解説しました。

ビューで用意したデータを、実際に画面のHTMLへ表示するのがテンプレートの役割です。

今回はDjango 5系を前提に、テンプレートの基本構文とよく使うテンプレートタグをまとめて解説します。

テンプレートとは?

HTMLに「Pythonの値」を差し込む仕組み

テンプレートとは、HTMLの中にPythonの値やちょっとしたロジックを埋め込めるファイルのことです。

拡張子は.htmlのままですが、中身にはDjango独自の記法が混ざります。

たとえば、ビューから渡されたnameという変数を画面に表示したい場合、こう書きます。

<p>こんにちは、{{ name }}さん!</p>

{{ }}で囲んだ部分が変数として展開され、実際の値に置き換わります。

テンプレートファイルの置き場所

Djangoはデフォルトで、各アプリのtemplates/フォルダの中を探しに行きます。

アプリ名がblogなら、次のような構成が一般的です。

blog/
├── templates/
│   └── blog/
│       └── index.html
├── views.py

わざわざblog/index.htmlのようにアプリ名のフォルダを一段挟むのがポイントです。

これを名前空間化と呼びます。

複数のアプリに同じindex.htmlという名前のファイルがあっても、テンプレート名をblog/index.htmlのように指定すれば、Djangoがどちらのファイルか正しく区別できるようになります。

筆者は最初この一段深いフォルダ構成を省略してしまい、別アプリの同名テンプレートが誤って読み込まれるという事故に遭いました。

TemplateDoesNotExistとは逆に、こちらはエラーにならず静かに違うHTMLが表示されるので、実は結構気づきにくいトラブルです。

テンプレートの基本構文

変数の表示:{{ }}

先ほど紹介した{{ 変数名 }}が最も基本的な構文です。

ビュー側でrender()にコンテキスト(辞書データ)を渡すことで、テンプレートから参照できるようになります。

# views.py
from django.shortcuts import render

def index(request):
    context = {"name": "かつコーチ"}
    return render(request, "blog/index.html", context)

タグの実行:{% %}

ifforのような処理を書くときは、{% %}を使います。

{% if user_count > 0 %}
  <p>{{ user_count }}人が登録しています。</p>
{% else %}
  <p>まだ誰も登録していません。</p>
{% endif %}

{{ }}が「値を表示する」ためのもの、{% %}が「処理を実行する」ためのもの、と役割で覚えると混同しにくいです。

よく使うテンプレートタグ・フィルタ

{% for %}で一覧表示する

一覧ページを作るときによく使うのがforタグです。

<ul>
  {% for post in posts %}
    <li>{{ post.title }}</li>
  {% empty %}
    <li>投稿がありません</li>
  {% endfor %}
</ul>

{% empty %}は、postsが空のリストだった場合にだけ表示される特別なブロックです。

「データが0件のときの表示」を用意し忘れると、本番で空白の画面が出てユーザーを不安にさせてしまうので、忘れずに書く習慣をつけましょう。

{% url %}でリンクをハードコーディングしない

リンク先を<a href="/blog/1/">のように直接書いてしまうと、後でURLの構造を変えたときに全ページを書き換える羽目になります。

{% url %}タグを使えば、urls.pyで設定したnameからURLを自動生成できます。

<a href="{% url 'blog:detail' post.id %}">{{ post.title }}</a>

フィルタで表示形式を整える

値を加工して表示したいときは、パイプ|でフィルタをつなげます。

<p>{{ post.created_at|date:"Y年n月j日" }}</p>
<p>{{ post.content|truncatewords:30 }}</p>
<p>{{ post.title|default:"タイトル未設定" }}</p>

dateで日付の表示形式を整えたり、truncatewordsで本文を途中まで省略表示したりと、実務でも頻出のフィルタです。

つまずきやすいポイント

変数名のスペルミスに気づきにくい

❌ Before:変数名を間違えているのにエラーが出ない

<p>こんにちは、{{ nmae }}さん!</p>

Djangoのテンプレートは、存在しない変数を参照してもエラーを出さず、単に空文字として扱います。

{{ nmae }}のようにスペルミスをしても、画面が真っ白になるだけでコンソールにも何も出ません。

筆者はこれで30分ほど「なぜ名前が表示されないんだ」と原因を探し回った経験があります。

✅ After:settings.pyTEMPLATES設定でDEBUG=Trueのまま開発し、目視で丁寧に確認する

<p>こんにちは、{{ name }}さん!</p>

対策として決定的な仕組みはありませんが、開発中はコンテキストに渡す変数名とテンプレート側の変数名を、コピー&ペーストで揃える癖をつけると事故を減らせます。

テンプレートの再利用:継承とinclude

{% extends %}で共通レイアウトを使い回す

ヘッダーやフッターを毎回コピーするのは非効率です。

base.htmlという共通テンプレートを用意し、各ページはそれを継承する形にします。

<!-- base.html -->
<html>
<body>
  <header>共通ヘッダー</header>
  {% block content %}{% endblock %}
  <footer>共通フッター</footer>
</body>
</html>
<!-- index.html -->
{% extends "blog/base.html" %}

{% block content %}
  <p>ここがページごとの中身です。</p>
{% endblock %}

{% block %}で「差し替え可能な範囲」を宣言しておき、子テンプレート側で中身を上書きするイメージです。

{% include %}で部品化する

ナビゲーションメニューのような小さな部品は、includeで切り出すと管理しやすくなります。

{% include "blog/_navbar.html" %}

継承は「ページ全体の骨組みを共有する」場合、includeは「一部の小さなパーツを再利用する」場合、と使い分けると設計が整理しやすいです。

まとめ

この記事のポイント

  • テンプレートは{{ }}で変数表示、{% %}でタグ処理を行う
  • テンプレートファイルはアプリ名で名前空間を分けて配置する
  • {% url %}とフィルタを活用すると、保守しやすいテンプレートになる
  • 存在しない変数は空文字扱いになり、エラーが出ない点に注意する
  • {% extends %}{% include %}で共通部分を使い回す

次に読むべき記事

  • クラスベースビュー(CBV)の基本と使いどころ
  • urls.pyでルーティングを設定する基本
  • 関数ベースビュー(FBV)の書き方

タグ: Django, 初心者向け, フレームワーク基礎

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