Django基礎 #7 Django Admin とビルトイン認証

読了 7分

#1 Django とは でした約束 — Admin が無料で付いてくる — を回収する番です。今回は Django が自動で作ってくれる管理者ページと、同じところで付いてくるビルトイン認証 (User、ログイン、権限) を一息で見ます。シリーズの最終回です。

Admin — モデル登録ワンライナー #

blog/admin.py に 1 行。

blog/admin.py
from django.contrib import admin

from .models import Post

admin.site.register(Post)

これで終わりです。runserver を立ち上げて http://127.0.0.1:8000/admin/ に行くと — まだログインページですね。superuser が必要です。

Superuser を作る #

superuser の生成
uv run python manage.py createsuperuser

対話形式で username / email / password を入力すれば終わり。これで /admin/ にそのアカウントでログインできます。

ログインすると — Auth (User、Group)、Blog (Post) が左側のメニューに。Post をクリックすると自動で作られた CRUD 画面 が見えます。一覧、生成、修正、削除、検索まで。

コードを 1 行も追加していないのに。これが Django のもっとも強力な約束です。

ModelAdmin — Admin 画面のカスタマイズ #

デフォルト画面は単調です (Post object (1)Post object (2) …)。ModelAdmin で豊かにします。

blog/admin.py
from django.contrib import admin

from .models import Post, Tag, Comment


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    # 一覧画面
    list_display = ("title", "author", "is_published", "created_at")
    list_filter = ("is_published", "created_at", "author")
    search_fields = ("title", "content")
    date_hierarchy = "created_at"
    ordering = ("-created_at",)
    list_editable = ("is_published",)
    list_per_page = 25

    # 詳細画面
    fields = ("title", "author", "content", "tags", "is_published")
    readonly_fields = ("created_at", "updated_at")
    filter_horizontal = ("tags",)        # ManyToMany ウィジェットの改善
    autocomplete_fields = ("author",)    # FK が多いときの検索オートコンプリート
    prepopulated_fields = {"slug": ("title",)}  # title → slug 自動生成


@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ("name", "slug")
    search_fields = ("name",)
    prepopulated_fields = {"slug": ("name",)}


admin.site.register(Comment)

よく使うオプション一覧:

オプション効果
list_display一覧に表示するカラム
list_filter右サイドバーのフィルタ
search_fields上部の検索ボックス (対象フィールド)
date_hierarchy日付でドリルダウンする上部ナビ
orderingデフォルトのソート
list_editable一覧から直接修正可能
list_per_pageページあたり件数
fields / fieldsets詳細画面のフィールド配置
readonly_fields読み取り専用フィールド
filter_horizontal / filter_verticalM2M の二段ウィジェット
autocomplete_fieldsFK / M2M のオートコンプリート (対象モデルに search_fields が必要)
prepopulated_fields別フィールドから自動入力 (slug が一般的)

@admin.register(Post) デコレータは admin.site.register(Post, PostAdmin) と同じです。よりすっきりします。

Inline — 親画面で子も一緒に編集 #

Post の詳細画面でその記事の Comment を一緒に表示したい場合。

Inline
from django.contrib import admin

from .models import Post, Comment


class CommentInline(admin.TabularInline):    # or StackedInline
    model = Comment
    extra = 1                                 # 空の行を 1 つ追加
    fields = ("author", "body", "created_at")
    readonly_fields = ("created_at",)


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "author")
    inlines = [CommentInline]
  • TabularInline — テーブル形式 (1 行に 1 コメント)
  • StackedInline — カード形式 (フィールドを縦に並べる)

カート / 注文項目 / 投稿の添付ファイルのように 親 ↔ 子 関係が明確な場合によく合います。

Admin の見た目以上の強さ #

  • 検索 + フィルタ + ページング が自動
  • 変更履歴 — 誰がいつ何を変えたかを記録 (Recent Actions)
  • 権限体系 — モデル単位の add/change/delete/view 権限が自動生成
  • 多言語 — ユーザーの LANGUAGE_CODE に合わせて UI が変わる
  • i18n + RTL 対応

小さな社内ツールなら Admin だけで運用画面が完結します。Admin そのものを運用ツールとして使う会社 も多いです。

Admin セキュリティ — 運用の基本 #

  • /admin/ のパスをそのまま置かず変更 (path("private/", admin.site.urls))
  • IP 制限 (Nginx またはミドルウェア)
  • 2 段階認証 (django-otpdjango-allauth-2fa)
  • superuser は本当に必要な人だけ

詳しい運用セキュリティは 上級 #7 デプロイセキュリティ で。

ビルトイン認証 — django.contrib.auth #

Admin の基盤がまさに django.contrib.auth です。すでに INSTALLED_APPS に入っており、migrate 時に UserGroupPermission テーブルが作られています。

User モデル #

基本 User の使用
from django.contrib.auth import get_user_model

User = get_user_model()   # コンベンション — settings.AUTH_USER_MODEL に従う

user = User.objects.create_user(
    username="curtis",
    email="me@example.com",
    password="secret123",   # 自動でハッシュ化される
)

user.set_password("new-password")    # パスワード変更
user.save()

user.check_password("new-password")  # → True

create_userset_password がパスワードハッシュ (PBKDF2 がデフォルト) を自動で。絶対に平文で保存してはいけません。

カスタム User — 最初から推奨 #

デフォルト User も動作しますが、新しいプロジェクトは最初からカスタム User モデルで始める のがコンベンションです。後で変えるのが非常に難しいです。

accounts/models.py — カスタム User
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to="avatars/", blank=True, null=True)
config/settings.py
AUTH_USER_MODEL = "accounts.User"

深いカスタマイズ (メールログイン、権限など) は 中級 #4 ユーザー / 権限 で。

ログイン / ログアウト — ビルトイン view #

直接作る必要はありません。django.contrib.auth.urls に全部あります。

config/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("accounts/", include("django.contrib.auth.urls")),
    path("blog/", include("blog.urls")),
]

include("django.contrib.auth.urls") の 1 行に付いてくる URL:

名前パス何をするか
login/accounts/login/ログイン
logout/accounts/logout/ログアウト
password_change/accounts/password_change/パスワード変更
password_change_done/accounts/password_change/done/変更完了
password_reset/accounts/password_reset/パスワード再設定 (メール)
password_reset_done / confirm / complete再設定フロー

view はすべてあるのに テンプレートは自分で 作らなければなりません。

templates/registration/login.html
{% extends "base.html" %}

{% block content %}
<h1>ログイン</h1>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">ログイン</button>
</form>
{% if form.errors %}
  <p>ID またはパスワードが間違っています。</p>
{% endif %}
<p><a href="{% url 'password_reset' %}">パスワードを忘れた方はこちら</a></p>
{% endblock %}

このファイル 1 つでログインが動作します。logout / password_* も同じ場所 (templates/registration/) に作ります。

ログイン後のリダイレクト #

config/settings.py
LOGIN_URL = "/accounts/login/"
LOGIN_REDIRECT_URL = "/blog/"
LOGOUT_REDIRECT_URL = "/blog/"

会員登録は自分で #

ビルトインは ログイン / ログアウト / パスワード だけ提供します。会員登録は自分で作るか django-allauth のようなパッケージを使います。

accounts/views.py — 自作 signup
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import redirect, render


def signup(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect("blog:post_list")
    else:
        form = UserCreationForm()
    return render(request, "registration/signup.html", {"form": form})

UserCreationForm もビルトイン — username + password + password 確認フィールドを備えた ModelForm です。

@login_required — ログイン保護 #

view 関数の上にデコレータを 1 行。

blog/views.py
from django.contrib.auth.decorators import login_required


@login_required
def post_new(request):
    ...

ログインしていないユーザーがアクセスすると自動で LOGIN_URL にリダイレクト (本来行こうとしていた URL は ?next=... クエリで付き、ログイン後に戻ってきます)。

request.user — 現在のユーザー #

view の中で
def my_page(request):
    if request.user.is_authenticated:
        username = request.user.username
        ...
テンプレートで
{% if user.is_authenticated %}
  <p>{{ user.username }} さんようこそ。</p>
  <a href="{% url 'logout' %}">ログアウト</a>
{% else %}
  <a href="{% url 'login' %}">ログイン</a>
{% endif %}

request.user はログインしていなければ AnonymousUser インスタンスです (is_authenticatedFalse)。だから if request.user: のような検査は意味がありません — 必ず is_authenticated で。

権限 — @permission_required / user.has_perm #

Django はモデルごとに自動的に 4 つの権限を作ります — add_<model>change_<model>delete_<model>view_<model>

権限チェック
from django.contrib.auth.decorators import permission_required


@permission_required("blog.delete_post", raise_exception=True)
def post_delete(request, post_id):
    ...
コードで直接
if request.user.has_perm("blog.change_post"):
    ...
テンプレートで
{% if perms.blog.change_post %}
  <a href="...">修正</a>
{% endif %}

Group で権限のまとまりを作り、ユーザーにグループを割り当てるのが運用パターンです。深い権限モデルは 中級 #4 ユーザー / 権限 で。

まとめ #

今回つかんだもの:

  • admin.site.register(Model) の 1 行で自動 CRUD UI
  • createsuperuser で管理者アカウント
  • ModelAdminlist_displaylist_filtersearch_fieldsdate_hierarchy、…
  • Inline — 親画面で子も一緒に編集
  • ビルトイン Usercreate_userset_passwordcheck_password
  • 新しいプロジェクトは 最初からカスタム User モデル を推奨
  • include("django.contrib.auth.urls") でログイン / ログアウト / パスワードフロー
  • テンプレートは templates/registration/ に自分で
  • @login_requiredrequest.user.is_authenticated
  • 権限 — モデル別に自動生成、@permission_requireduser.has_perm、テンプレート perms.app.codename
  • Admin は運用ツールとしてそのまま使えるレベル — ただしセキュリティ (パス変更、2FA、IP 制限) は必須

シリーズのまとめ #

7 編を一息で整理すると:

  1. #1 Django とは — フルスタックモノリスの位置
  2. #2 プロジェクトのセットアップ — uv + startproject + startapp
  3. #3 Models と ORM 基礎 — モデル、マイグレーション、QuerySet
  4. #4 URL と Views — URLconf、FBV、render
  5. #5 Templates と静的ファイル — テンプレート継承、static
  6. #6 Forms と ModelForm — フォーム検証、ModelForm、ファイルアップロード
  7. #7 Admin と認証 — 自動 CRUD、ログイン、権限 ← 今回

この 7 編で小さなブログを 1 軒建てるのに必要な部品がすべて揃いました。1 人で小さなサイドプロジェクトを作れるレベルです。

次のシリーズ Django 中級 #1 CBV の深さ では FBV の繰り返しパターンを縮める クラスベースビュー を扱います。ListView、DetailView、CreateView、UpdateView、DeleteView の 5 つで同じ CRUD をはるかに短く書ける流れを見ます。その上に 中級 #2 ORM 中級 の select_related / prefetch_related、中級 #7 テスト まで — 1 軒を本当に運用できる形にしていく流れです。

ここまで付いてきてくださった方に感謝します。フルスタック 1 軒を建てる楽しさが始まりますように。

X