タイトル: テンプレートで利用する共通のcontextを定義する方法
SEOタイトル: Django Context Processor 完全ガイド(全テンプレに共通変数を注入)
この本文には実行可能なタグ等が含まれるため、安全のため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><strong>Context Processor</strong> = 全テンプレートに自動で渡される変数を作る仕組み</li><li>実装は <strong><code>def my_processor(request): return {...}</code></strong> という関数を 1 つ書くだけ</li><li>登録は <strong><code>settings.py</code> の TEMPLATES → OPTIONS → context_processors</strong> に文字列パスで追加</li><li>標準で <strong>request / user / perms / messages / debug</strong> 等のプロセッサが用意されている</li><li>典型用途: <strong>サイト名 / グローバルメニュー / 通知数 / 環境バッジ / 認証状態</strong></li></ul></td></tr></table>
<h2>Context Processor とは</h2>
<p>Django のテンプレートは、ビューで <code>render(request, 'template.html', context)</code> に渡した辞書 <code>context</code> しか参照できないのが基本です。しかし「ヘッダーに常に表示するサイト名」「全画面共通のお知らせ件数」など、全テンプレに共通で渡したい変数があります。これを毎回ビューで <code>context['site_name'] = ...</code> と書くのは無駄なので、<strong>Context Processor</strong> という仕組みで一括注入します。</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">[リクエスト]
↓
[ビュー関数] → render(request, 'page.html', {'posts': posts})
↓
[テンプレートエンジン] が render を解決する直前に
context_processors を全部実行して、戻り値の辞書を context に merge
↓
[最終 context] = {'posts': posts, 'site_name': 'My Blog', 'user': <User>, ...}
↓
[テンプレート] で {{ site_name }} {{ user.username }} が使える</code></pre>
<h2>標準で用意されている Context Processor</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>Processor</th><th>テンプレに渡る変数</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>django.template.context_processors.debug</code></td><td><code>debug</code>, <code>sql_queries</code></td><td>DEBUG モード判定</td></tr>
<tr><td><code>django.template.context_processors.request</code></td><td><code>request</code></td><td>テンプレで <code>{{ request.path }}</code> 等</td></tr>
<tr><td><code>django.contrib.auth.context_processors.auth</code></td><td><code>user</code>, <code>perms</code></td><td>認証ユーザ / 権限</td></tr>
<tr><td><code>django.contrib.messages.context_processors.messages</code></td><td><code>messages</code></td><td>フラッシュメッセージ</td></tr>
<tr><td><code>django.template.context_processors.csrf</code></td><td><code>csrf_token</code></td><td>CSRF トークン(標準で自動有効)</td></tr>
<tr><td><code>django.template.context_processors.i18n</code></td><td><code>LANGUAGES</code>, <code>LANGUAGE_CODE</code></td><td>多言語対応</td></tr>
<tr><td><code>django.template.context_processors.static</code></td><td><code>STATIC_URL</code></td><td>静的ファイル URL</td></tr>
<tr><td><code>django.template.context_processors.media</code></td><td><code>MEDIA_URL</code></td><td>メディア URL</td></tr>
</tbody>
</table>
<h2>カスタム Context Processor を作る</h2>
<h3>ステップ 1: 関数を書く</h3>
<p>アプリ内に <code>context_processors.py</code> を作成し、<code>request</code> を受け取って辞書を返す関数を定義します:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># blog/context_processors.py
from django.conf import settings
def site_settings(request):
"""全テンプレに渡すサイト共通設定"""
return {
'site_name': 'My Blog',
'site_description': 'Django で作ったブログ',
'site_url': 'https://example.com',
'copyright_year': 2026,
'env': settings.ENV_NAME,
}
def global_menu(request):
"""サイドバー / ヘッダーの共通メニュー"""
menu_items = [
{'name': 'ホーム', 'url': '/'},
{'name': '記事一覧', 'url': '/blog/'},
{'name': 'カテゴリ', 'url': '/blog/category/'},
{'name': 'お問い合わせ', 'url': '/contact/'},
]
return {'global_menu': menu_items}
def notifications(request):
"""ログインユーザの未読通知数"""
if request.user.is_authenticated:
unread = request.user.notifications.filter(read_at__isnull=True).count()
else:
unread = 0
return {'unread_notification_count': unread}</code></pre>
<h3>ステップ 2: settings.py に登録</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># mysite/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'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',
# ★ カスタムを追加
'blog.context_processors.site_settings',
'blog.context_processors.global_menu',
'blog.context_processors.notifications',
],
},
},
]</code></pre>
<h3>ステップ 3: テンプレで使う</h3>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# templates/base.html #}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{{ site_name }}{% endblock %}</title>
<meta name="description" content="{{ site_description }}">
</head>
<body class="env-{{ env }}">
<header>
<h1>{{ site_name }}</h1>
<nav>
{% for item in global_menu %}
<a href="{{ item.url }}">{{ item.name }}</a>
{% endfor %}
</nav>
{% if user.is_authenticated %}
<span>{{ user.username }} さん</span>
<span class="badge">通知 {{ unread_notification_count }}</span>
{% endif %}
</header>
<main>{% block content %}{% endblock %}</main>
<footer>© {{ copyright_year }} {{ site_name }}</footer>
</body>
</html></code></pre>
<h2>標準 Processor の威力: request</h2>
<p><code>django.template.context_processors.request</code> を入れておくと、全テンプレで <code>{{ request }}</code> が使えます。これは非常に便利:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html">{# 現在の URL #}
{{ request.path }}
{# ナビゲーションのアクティブ判定 #}
<a href="/blog/" class="{% if request.path == '/blog/' %}active{% endif %}">記事</a>
{# GET パラメータ #}
{{ request.GET.q }}
{# ユーザー(authenticated_user processor 経由でも取れる) #}
{{ request.user.username }}</code></pre>
<h2>パフォーマンスの注意</h2>
<p>Context Processor は<strong>全てのテンプレート描画で毎回実行される</strong>ので、重い処理を書くと全画面が遅くなります:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># ❌ 危険: 全画面で SQL を発行する
def heavy_processor(request):
return {
'all_categories': Category.objects.all(), # 毎回 SELECT
'popular_tags': Tag.objects.annotate(...)[:10], # 集計クエリ
}
# ✅ 改善: キャッシュ
from django.core.cache import cache
def cached_processor(request):
categories = cache.get('all_categories')
if categories is None:
categories = list(Category.objects.all())
cache.set('all_categories', categories, 60 * 5) # 5 分
return {'all_categories': categories}
# ✅ 改善: 不要なテンプレでは渡さない
# → 該当テンプレでビュー側 context に入れる方が正解の場合も多い</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"># tests/test_context_processors.py
from django.test import RequestFactory, TestCase
from blog.context_processors import site_settings, notifications
from django.contrib.auth.models import User
class ContextProcessorTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_site_settings(self):
request = self.factory.get('/')
ctx = site_settings(request)
self.assertEqual(ctx['site_name'], 'My Blog')
def test_notifications_anonymous(self):
request = self.factory.get('/')
request.user = AnonymousUser()
self.assertEqual(notifications(request)['unread_notification_count'], 0)</code></pre>
<h2>FAQ</h2>
<p><strong>Q: ビュー側で同名キーを渡したら?</strong><br />
A: <strong>ビュー側の値が優先</strong>されます(後勝ち)。Context Processor → ビュー → テンプレの順で合成されます。</p>
<p><strong>Q: 関数の戻り値以外、たとえば例外は?</strong><br />
A: Context Processor で例外を投げると、テンプレ描画全体が失敗します。try/except でフェイルセーフ実装を。</p>
<p><strong>Q: クラスベースビューでも使える?</strong><br />
A: もちろん。Context Processor は <code>render</code> や <code>TemplateResponse</code> 経由で発火するので、CBV / FBV を問わず適用されます。</p>