タイトル: テンプレート(Template)
SEOタイトル: Django テンプレート完全ガイド — 構文 / 継承 / カスタムタグ
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>Django テンプレートは HTML の中に <strong>変数 <code>{{ var }}</code></strong>・<strong>タグ <code>{% ... %}</code></strong>・<strong>フィルタ <code>{{ var|filter }}</code></strong> を埋め込むテンプレート言語</li><li>ファイルは <code>app/templates/app/index.html</code> に配置。<code>render(request, "app/index.html", context)</code> で呼ぶ</li><li>継承: <code>{% extends "base.html" %}</code> + <code>{% block content %}{% endblock %}</code> で共通レイアウト</li><li>制御: <code>{% if %}</code> / <code>{% for %}</code> / <code>{% url "name" %}</code> / <code>{% include "_partial.html" %}</code> / <code>{% static "path" %}</code></li><li>カスタムタグ/フィルタは <code>templatetags/</code> 配下に作成し <code>{% load %}</code> で読み込む</li></ul></td></tr></table>
<h2>Django テンプレートとは</h2>
<p>Django テンプレートは、HTML と <strong>テンプレート構文</strong>を混在させて、動的な HTML を組み立てる仕組みです。プログラマでなくてもデザイナが触れるよう、Python の構文ではなく独自の DSL を採用しています。</p>
<h2>配置と呼び出し</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">myapp/
├── views.py
├── models.py
└── templates/
└── myapp/
├── base.html
├── index.html
└── detail.html</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># myapp/views.py
from django.shortcuts import render
def index(request):
context = {
"title": "記事一覧",
"articles": Article.objects.all(),
"user_name": "taro",
}
return render(request, "myapp/index.html", context)</code></pre>
<h2>変数・フィルタ・タグの 3 構文</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# 1. 変数 #}
<h1>{{ title }}</h1>
<p>こんにちは、{{ user_name }} さん</p>
{# 2. フィルタ #}
<p>{{ name|upper }}</p> {# 大文字 #}
<p>{{ body|truncatechars:50 }}</p> {# 50 文字で切る #}
<p>{{ price|floatformat:2 }}</p> {# 小数 2 桁 #}
<p>{{ created_at|date:"Y-m-d H:i" }}</p> {# 日付フォーマット #}
<p>{{ html|safe }}</p> {# エスケープ無効 (安全な場合のみ) #}
<p>{{ var|default:"未設定" }}</p> {# null/空のときの代替値 #}
{# 3. タグ #}
{% if articles %}
<ul>
{% for a in articles %}
<li>{{ a.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>記事はありません</p>
{% endif %}</code></pre>
<h2>主要フィルタ一覧</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>フィルタ</th><th>用途</th><th>例</th></tr></thead>
<tbody>
<tr><td><code>upper</code> / <code>lower</code></td><td>大文字/小文字</td><td><code>{{ s|upper }}</code></td></tr>
<tr><td><code>title</code></td><td>各単語の頭文字を大文字</td><td><code>{{ s|title }}</code></td></tr>
<tr><td><code>length</code></td><td>長さ</td><td><code>{{ list|length }}</code></td></tr>
<tr><td><code>truncatechars:N</code></td><td>N 文字で切る</td><td><code>{{ s|truncatechars:50 }}</code></td></tr>
<tr><td><code>date:"Y-m-d"</code></td><td>日付フォーマット</td><td><code>{{ dt|date:"Y-m-d" }}</code></td></tr>
<tr><td><code>floatformat:N</code></td><td>小数点 N 桁</td><td><code>{{ p|floatformat:2 }}</code></td></tr>
<tr><td><code>default:"-"</code></td><td>null/空時の代替</td><td><code>{{ v|default:"-" }}</code></td></tr>
<tr><td><code>safe</code></td><td>HTML エスケープを無効化</td><td><code>{{ html|safe }}</code></td></tr>
<tr><td><code>escape</code></td><td>HTML エスケープ</td><td><code>{{ s|escape }}</code></td></tr>
<tr><td><code>linebreaks</code></td><td>改行を <br> / <p> に</td><td><code>{{ s|linebreaks }}</code></td></tr>
<tr><td><code>add:N</code></td><td>加算</td><td><code>{{ n|add:1 }}</code></td></tr>
<tr><td><code>join:","</code></td><td>配列を結合</td><td><code>{{ list|join:"," }}</code></td></tr>
</tbody>
</table>
<h2>主要タグ一覧</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# for ループ #}
{% for a in articles %}
{{ forloop.counter }}: {{ a.title }}
{% empty %}
記事なし
{% endfor %}
{# if #}
{% if user.is_authenticated %}
ようこそ {{ user.username }} さん
{% elif user.is_anonymous %}
ゲストです
{% else %}
不明
{% endif %}
{# URL 逆引き #}
<a href="{% url 'myapp:detail' article.id %}">詳細</a>
{# static ファイル #}
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'img/logo.png' %}" alt="ロゴ">
{# パーシャル取り込み #}
{% include "myapp/_card.html" with article=a %}
{# コメント #}
{# 一行コメント #}
{% comment %}
ブロックコメント
複数行
{% endcomment %}
{# CSRF トークン (フォーム必須) #}
<form method="post">
{% csrf_token %}
...
</form>
{# 変数定義 #}
{% with total=cart.items|length %}
カート: {{ total }} 件
{% endwith %}</code></pre>
<h2>テンプレート継承 (extends + block)</h2>
<p>レイアウト共通化の必須テクニックです。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# templates/base.html #}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{% block title %}サイト名{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body>
<header>{% include "_nav.html" %}</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>© 2026</footer>
{% block extra_js %}{% endblock %}
</body>
</html></code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# templates/myapp/index.html #}
{% extends "base.html" %}
{% load static %}
{% block title %}記事一覧 | {{ block.super }}{% endblock %}
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/article.css' %}">
{% endblock %}
{% block content %}
<h1>記事一覧</h1>
<ul>
{% for a in articles %}
<li><a href="{% url 'myapp:detail' a.id %}">{{ a.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}</code></pre>
<h2>カスタムタグ / カスタムフィルタ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># myapp/templatetags/myfilters.py
from django import template
register = template.Library()
@register.filter
def yen(value):
"""1234 → ¥1,234"""
return f"¥{value:,}"
@register.simple_tag
def current_year():
from datetime import date
return date.today().year
@register.inclusion_tag("myapp/_card.html")
def article_card(article):
return {"article": article}</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# 利用側 #}
{% load myfilters %}
<p>{{ price|yen }}</p> {# ¥1,234 #}
<p>© {% current_year %} 会社名</p>
{% article_card a %}</code></pre>
<h2>セキュリティ — エスケープ</h2>
<p>Django テンプレートは<strong>デフォルトで HTML エスケープ</strong>します:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# context: {"user_input": "<script>alert(1)</script>"} #}
{{ user_input }}
{# → <script>alert(1)</script> に変換され安全 #}
{{ user_input|safe }}
{# → そのまま <script> として出力される (XSS の可能性、信頼できるデータのみ) #}
{% autoescape off %}
{{ user_input }}
{% endautoescape %}
{# → ブロック全体でエスケープ無効 #}</code></pre>
<h2>FAQ</h2>
<p><strong>Q: テンプレートが見つからないと言われる</strong><br />
A: <code>settings.py</code> の <code>TEMPLATES.DIRS</code> と <code>APP_DIRS=True</code>、<code>INSTALLED_APPS</code> にアプリが入っているかを確認。</p>
<p><strong>Q: Python のメソッド呼び出しは?</strong><br />
A: 引数なしなら自動呼び出し。<code>{{ user.get_full_name }}</code> (括弧不要)。引数ありはカスタムタグを作る。</p>
<p><strong>Q: Jinja2 を使いたい</strong><br />
A: <code>django.template.backends.jinja2.Jinja2</code> を <code>TEMPLATES</code> に設定すれば併用可能。</p>