{{ article.title }}
コメント ({{ comments|length }})
{% for c in comments %}コメントはまだありません。
{% endfor %}ページの作成
親となるページを選択してください。
| この記事の要点 |
|
# myproject/settings.py
import os
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # プロジェクト全体共通
'APP_DIRS': True, # 各アプリの templates/ も探索
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
myproject/
├── manage.py
├── myproject/
│ └── settings.py
├── templates/ ← プロジェクト共通(DIRS で指定)
│ ├── base.html
│ └── 404.html
└── myapp/
├── views.py
└── templates/ ← アプリ別(APP_DIRS=True で自動)
└── myapp/ ← ネームスペース推奨
├── article_list.html
└── article_detail.html
アプリの templates/ 直下にファイルを置くと、他アプリの同名ファイルと衝突するため、templates/myapp/ のようにアプリ名のサブディレクトリを挟むのが Django 標準のベストプラクティスです。
{# templates/base.html #}
{% block title %}MySite{% endblock %}
{% block extra_head %}{% endblock %}
{% include "header.html" %}
{% block content %}{% endblock %}
{% include "footer.html" %}
{% block extra_js %}{% endblock %}
{# templates/myapp/article_detail.html #}
{% extends "base.html" %}
{% load static %}
{% block title %}{{ article.title }} | MySite{% endblock %}
{% block extra_head %}
{% endblock %}
{% block content %}
{{ article.title }}
{{ article.body|linebreaks }}
コメント ({{ comments|length }})
{% for c in comments %}
{{ c.author }}: {{ c.body }}
{% empty %}
コメントはまだありません。
{% endfor %}
{% endblock %}
{# templates/header.html #}
MySite
{# 呼び出し側 #}
{% include "header.html" %}
{# 変数を渡しながら include #}
{% include "card.html" with title="ようこそ" body=user.name only %}
{# only を付けると親コンテキストを渡さない #}
| タグ | 意味 |
|---|---|
{% if %} / {% elif %} / {% else %} | 条件分岐 |
{% for x in xs %} / {% empty %} | ループ |
{% url 'name' arg %} | URL 逆引き |
{% csrf_token %} | CSRF トークン |
{% block %} / {% extends %} | 継承 |
{% include %} | 部分挿入 |
{% load %} | カスタムタグライブラリ読込 |
{% with %} | 一時変数 |
{% spaceless %} | タグ間の空白除去 |
{% autoescape on/off %} | HTML エスケープ切替 |
{{ name|upper }} {# 大文字 #}
{{ body|truncatechars:100 }} {# 100 文字で省略 #}
{{ created_at|date:"Y-m-d H:i" }} {# 日付フォーマット #}
{{ price|floatformat:2 }} {# 小数2位 #}
{{ html|safe }} {# エスケープしない(XSS 注意) #}
{{ list|length }} {# 件数 #}
{{ value|default:"N/A" }} {# 値が偽なら N/A #}
{{ text|linebreaks }} {# 改行を
に #}
{{ user.name|escape }} {# 明示的にエスケープ #}
Django テンプレートはデフォルトで全変数を HTML エスケープします。 をそのまま埋めても文字列として表示されます。
{# 通常: エスケープされる #}
{{ user_input }}
{# → <script>alert(1)</script> #}
{# 明示的にエスケープしない(XSS 注意) #}
{{ html_content|safe }}
{# ブロック単位で切替 #}
{% autoescape off %}
{{ trusted_html }}
{% endautoescape %}
safe や autoescape off はサニタイズ済みデータにのみ使うこと。ユーザー入力に直接使うと XSS の温床になります。
myapp/
└── templatetags/
├── __init__.py
└── myapp_extras.py# myapp/templatetags/myapp_extras.py
from django import template
register = template.Library()
@register.filter
def yen(value):
""" 1000000 → 1,000,000 円 """
try:
return f"{int(value):,} 円"
except (TypeError, ValueError):
return value
@register.simple_tag
def current_year():
import datetime
return datetime.date.today().year
@register.inclusion_tag('myapp/_pagination.html')
def show_pagination(page_obj):
return {'page_obj': page_obj}{# テンプレート側で読み込み #}
{% load myapp_extras %}
{{ order.amount|yen }} {# → 1,000,000 円 #}
© {% current_year %} MySite
{% show_pagination page_obj %}
TEMPLATES.DIRS リスト(順序通り)APP_DIRS=True なら INSTALLED_APPS の順に各アプリの templates/先に見つかったものが使われます。サードパーティアプリのテンプレートを上書きしたい場合は、DIRS に書いたプロジェクト共通 templates/ 配下に同じパスで配置すれば優先されます。
# 例: django.contrib.admin/templates/admin/index.html を上書きしたい
templates/
└── admin/
└── index.html ← これがあれば admin デフォルトを上書き
# myapp/views.py
from django.shortcuts import render
from .models import Article
def article_detail(request, slug):
article = Article.objects.get(slug=slug)
return render(request, 'myapp/article_detail.html', {
'article': article,
'comments': article.comments.all(),
})
# CBV の場合
from django.views.generic import DetailView
class ArticleDetailView(DetailView):
model = Article
template_name = 'myapp/article_detail.html' # 省略時は myapp/article_detail.html が自動
context_object_name = 'article'
Q: テンプレートが見つからない (TemplateDoesNotExist)
A: DEBUG=True 状態でエラー画面に「探したパス一覧」が出る。配置ミス・APP_DIRS 設定漏れ・INSTALLED_APPS 登録漏れを確認。
Q: Jinja2 を使いたい
A: TEMPLATES の BACKEND を django.template.backends.jinja2.Jinja2 に。Django タグ ({% url %} 等) は使えなくなる。
Q: include vs extends の使い分け
A: extends は親レイアウトを継承して差し替える関係。include は同じ部品を埋め込む関係。ヘッダ・フッタ・カードは include、ページ全体は extends。
ページの作成
親となるページを選択してください。
子ページはありません
コメントを削除してもよろしいでしょうか?
掲示板