タイトル: テンプレート側のログイン判定
SEOタイトル: Django テンプレートでログイン判定する方法(is_authenticated / is_staff / グループ判定)
この本文には実行可能なタグ等が含まれるため、安全のため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><code>{% if user.is_authenticated %}</code></strong> でログイン済か判定するのが基本</li><li><code>request</code> をテンプレートに渡すのは <strong><code>django.template.context_processors.request</code></strong>、ユーザーは <code>auth</code> プロセッサ</li><li>管理者判定: <strong><code>user.is_staff</code></strong> / スーパーユーザー: <strong><code>user.is_superuser</code></strong></li><li>グループ判定: <strong><code>{% if user.groups.all|length > 0 %}</code></strong> または カスタムテンプレートタグ</li><li>ログイン強制は<strong>テンプレート側ではなく</strong> view 側の <code>@login_required</code> デコレータで</li><li>JSON API では <code>request.user.is_authenticated</code> を view で判定し、403 を返す</li></ul></td></tr></table>
<h2>基本: is_authenticated</h2>
<p>Django で「ログインしているか」を判定する最もシンプルな方法:</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 #}
<nav>
{% if user.is_authenticated %}
<span>こんにちは {{ user.username }} さん</span>
<a href="{% url 'logout' %}">ログアウト</a>
{% else %}
<a href="{% url 'login' %}">ログイン</a>
<a href="{% url 'signup' %}">新規登録</a>
{% endif %}
</nav></code></pre>
<p><code>user</code> 変数はテンプレート内で自動的に使えます(後述の context processor が設定されている場合)。</p>
<h2>context_processors の確認</h2>
<p>テンプレートから <code>user</code> や <code>request</code> を参照できるのは、Django の<strong>auth コンテキストプロセッサ</strong>が有効だからです:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># 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', # ← request 変数
'django.contrib.auth.context_processors.auth', # ← user / perms 変数
'django.contrib.messages.context_processors.messages',
],
},
},
]</code></pre>
<p><code>auth</code> プロセッサが入っていれば、すべてのテンプレートで <code>{{ user }}</code> と <code>{{ perms }}</code> が使えます。</p>
<h2>権限別の表示制御</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{% if user.is_authenticated %}
{# 一般会員向け #}
<a href="{% url 'mypage' %}">マイページ</a>
{% if user.is_staff %}
{# スタッフ向け(管理画面アクセス可) #}
<a href="{% url 'admin:index' %}">管理画面</a>
{% endif %}
{% if user.is_superuser %}
{# スーパーユーザー専用 #}
<a href="{% url 'system_settings' %}">システム設定</a>
{% endif %}
{% if user.email_verified %}
{# 自前で追加した属性も普通に使える #}
<span class="badge">認証済</span>
{% endif %}
{% else %}
<a href="{% url 'login' %}">ログイン</a>
{% endif %}</code></pre>
<h2>パーミッション判定: {% if perms.app.action %}</h2>
<p>Django の <code>django.contrib.auth</code> のパーミッションシステムに沿うなら:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-django">{# polls アプリの change_poll 権限を持っているか #}
{% if perms.polls.change_poll %}
<a href="{% url 'polls:edit' poll.id %}">編集</a>
{% endif %}
{# 複数 #}
{% if perms.polls.add_poll and perms.polls.delete_poll %}
<button>追加 & 削除</button>
{% endif %}</code></pre>
<h2>グループ判定</h2>
<p>「特定のグループに所属しているか」をテンプレートで判定したい場合:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python"># myapp/templatetags/auth_extras.py
from django import template
register = template.Library()
@register.filter(name='has_group')
def has_group(user, group_name):
return user.groups.filter(name=group_name).exists()</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 auth_extras %}
{% if user|has_group:"editors" %}
<a href="{% url 'cms:editor' %}">編集者ダッシュボード</a>
{% endif %}
{% if user|has_group:"premium" %}
<a href="{% url 'premium_content' %}">プレミアムコンテンツ</a>
{% endif %}</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/auth_extras.py
from django import template
register = template.Library()
@register.simple_tag(takes_context=True)
def can_edit(context, obj):
"""記事の作者 or staff なら True"""
request = context['request']
user = request.user
if not user.is_authenticated:
return False
if user.is_staff:
return True
return obj.author_id == user.id</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 auth_extras %}
{% can_edit article as user_can_edit %}
{% if user_can_edit %}
<a href="{% url 'article:edit' article.id %}">編集</a>
{% endif %}</code></pre>
<h2>ログイン強制は view 側で</h2>
<p>テンプレートで条件分岐するだけでは<strong>セキュリティ的に不十分</strong>です。データを返さないようにするには view 側で:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-python">from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
@login_required
def mypage(request):
return render(request, 'mypage.html')
# ログインしてかつパーミッションを持っているか
@permission_required('polls.add_poll', raise_exception=True)
def add_poll(request):
...
# カスタム条件
@user_passes_test(lambda u: u.is_authenticated and u.email_verified)
def verified_only(request):
...
# クラスベースビューなら LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
class MyPageView(LoginRequiredMixin, TemplateView):
template_name = 'mypage.html'
login_url = '/login/'</code></pre>
<h2>テンプレート内で次の URL を保持</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"><a href="{% url 'login' %}?next={{ request.path }}">ログイン</a>
{# ログインフォーム側 #}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{{ form.as_p }}
<button type="submit">ログイン</button>
</form></code></pre>
<h2>非匿名ユーザーの判定(旧 API)</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>user.is_authenticated</code></td><td>ログイン済か</td><td>1.10+ から property(() 不要)</td></tr>
<tr><td><code>user.is_anonymous</code></td><td>未ログインか</td><td>同上</td></tr>
<tr><td><code>user.is_active</code></td><td>アカウント有効か</td><td>停止アカウントは False</td></tr>
<tr><td><code>user.is_staff</code></td><td>管理画面アクセス可</td><td>admin/ で必要</td></tr>
<tr><td><code>user.is_superuser</code></td><td>全権限</td><td>perms 判定をバイパス</td></tr>
</tbody>
</table>
<h2>FAQ</h2>
<p><strong>Q: テンプレートで <code>user</code> が常に AnonymousUser になる</strong><br />
A: context processor の <code>django.contrib.auth.context_processors.auth</code> が抜けている可能性。settings.py を確認。または <code>render()</code> の代わりに自前の <code>RequestContext</code> を使い回している。</p>
<p><strong>Q: DRF のテンプレートビューでログイン判定したい</strong><br />
A: APIView では <code>request.user.is_authenticated</code> を view 内で判定し、<code>permission_classes = [IsAuthenticated]</code> を設定。</p>
<p><strong>Q: テンプレートで OR / AND を書きたい</strong><br />
A: <code>{% if user.is_staff or user.is_superuser %}</code> のように普通に or / and / not が使えます。</p>