この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: テンプレートで利用する共通の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, &apos;page.html&apos;, {&apos;posts&apos;: posts})
   ↓
[テンプレートエンジン] が render を解決する直前に
   context_processors を全部実行して、戻り値の辞書を context に merge
   ↓
[最終 context] = {&apos;posts&apos;: posts, &apos;site_name&apos;: &apos;My Blog&apos;, &apos;user&apos;: <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 {
        &apos;site_name&apos;: &apos;My Blog&apos;,
        &apos;site_description&apos;: &apos;Django で作ったブログ&apos;,
        &apos;site_url&apos;: &apos;https://example.com&apos;,
        &apos;copyright_year&apos;: 2026,
        &apos;env&apos;: settings.ENV_NAME,
    }


def global_menu(request):
    """サイドバー / ヘッダーの共通メニュー"""
    menu_items = [
        {&apos;name&apos;: &apos;ホーム&apos;, &apos;url&apos;: &apos;/&apos;},
        {&apos;name&apos;: &apos;記事一覧&apos;, &apos;url&apos;: &apos;/blog/&apos;},
        {&apos;name&apos;: &apos;カテゴリ&apos;, &apos;url&apos;: &apos;/blog/category/&apos;},
        {&apos;name&apos;: &apos;お問い合わせ&apos;, &apos;url&apos;: &apos;/contact/&apos;},
    ]
    return {&apos;global_menu&apos;: menu_items}


def notifications(request):
    """ログインユーザの未読通知数"""
    if request.user.is_authenticated:
        unread = request.user.notifications.filter(read_at__isnull=True).count()
    else:
        unread = 0
    return {&apos;unread_notification_count&apos;: 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 = [
    {
        &apos;BACKEND&apos;: &apos;django.template.backends.django.DjangoTemplates&apos;,
        &apos;DIRS&apos;: [BASE_DIR / &apos;templates&apos;],
        &apos;APP_DIRS&apos;: True,
        &apos;OPTIONS&apos;: {
            &apos;context_processors&apos;: [
                # 標準
                &apos;django.template.context_processors.debug&apos;,
                &apos;django.template.context_processors.request&apos;,
                &apos;django.contrib.auth.context_processors.auth&apos;,
                &apos;django.contrib.messages.context_processors.messages&apos;,
                # ★ カスタムを追加
                &apos;blog.context_processors.site_settings&apos;,
                &apos;blog.context_processors.global_menu&apos;,
                &apos;blog.context_processors.notifications&apos;,
            ],
        },
    },
]</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>&copy; {{ 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 == &apos;/blog/&apos; %}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 {
        &apos;all_categories&apos;: Category.objects.all(),       # 毎回 SELECT
        &apos;popular_tags&apos;: Tag.objects.annotate(...)[:10], # 集計クエリ
    }

# ✅ 改善: キャッシュ
from django.core.cache import cache

def cached_processor(request):
    categories = cache.get(&apos;all_categories&apos;)
    if categories is None:
        categories = list(Category.objects.all())
        cache.set(&apos;all_categories&apos;, categories, 60 * 5)  # 5 分
    return {&apos;all_categories&apos;: 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(&apos;/&apos;)
        ctx = site_settings(request)
        self.assertEqual(ctx[&apos;site_name&apos;], &apos;My Blog&apos;)

    def test_notifications_anonymous(self):
        request = self.factory.get(&apos;/&apos;)
        request.user = AnonymousUser()
        self.assertEqual(notifications(request)[&apos;unread_notification_count&apos;], 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>