この記事について
Djangoの認証機能の使い方メモで
以下記事の手順に多少追記したものです
https://qiita.com/ryo-keima/items/e6ce42bb4c11432ea829
Djangoの認証機能
公式ドキュメントが以下にあります
https://docs.djangoproject.com/ja/5.1/topics/auth/
仕組み
Djangoモデル (DB) にユーザー情報を保持しておいて、ユーザー名とパスワードで認証・認可する機能です。ユーザーモデルに権限情報を書いておけば、認証しているユーザーのプロパティとして簡単に取得できますので確実かつ簡単に扱えます
ログインするとセッションIDをユーザーブラウザ側のCookieに保持することでログイン状態を維持することができるという仕組みです
デフォルトではセッションCookieを使用しますが有効期限付きの永続Cookieに変更することも可能ですし、セッション情報をどこに保持するか設定したり、セッションタイムアウトを設定したりなどの詳しい設定もsettings.pyから指定可能です
できないこと
公式ドキュメントによると、Djangoの認証機能は一般的に求められる機能に限って提供するものであるので以下の機能については提供がないようです
・パスワード強度のチェック
・ログイン試行数の制限
・サードパーティに対する認証 (OAuthなど)
・オブジェクトレベルのパーミッション
上記についてはサードパーティのパッケージで実装する想定のようです
やり方
認証用のFormとViewを使うことで簡単に機能が実装できます
- ユーザーモデルをつくる
- UserCreationFormとCreateViewでユーザー登録機能をつくる
- AuthenticationFormとBaseLoginViewでログイン機能をつくる
- BaseLogoutViewでログアウト機能をつくる
完成品
完成したコードをgithubに置いています
https://github.com/haneya-studio/django_auth_sample
フォルダ構成は以下のようになりました

1. プロジェクトの作成
お試しで認証機能を付ける為のサンプルプロジェクトを作ります
既にアプリがあるならそちらで作ってもらっても大丈夫
1-1. プロジェクトをつくる
プロジェクトを作りたいディレクトリに移動してプロジェクトを作ります
django-admin startproject myapp .
1-2. migrateする
cd myapp
python manage.py migrate
1-3. 試しに起動
python manage.py runserver
2. ユーザーモデルをつくる
2-1. ユーザーモデルの選択肢
認証する為にユーザー情報を持つ必要があります
ユーザー情報を保持するモデルとして以下の3つが選べます
・Djangoデフォルトのユーザーモデルを使う
・カスタムユーザーモデルをつくる (AbstractUser)
・カスタムユーザーモデルをつくる (AbstractBaseUser, PermissionsMixin)
デフォルトのユーザーモデル
django.contrib.auth.models.Userで定義されているUserモデルをそのまま使います
Djangoにはデフォルトのユーザーモデルが定義されていて、ユーザー情報を登録することができるように以下のFieldが使用できます
フィールド名 | 型 | 説明 |
---|---|---|
username | CharField | ユーザー名(デフォルトでは一意制約あり) |
EmailField | メールアドレス | |
first_name | CharField | 名(オプション) |
last_name | CharField | 姓(オプション) |
password | CharField | ハッシュ化されたパスワード |
is_staff | BooleanField | 管理サイトにログイン可能か |
is_active | BooleanField | ユーザーが有効かどうか |
is_superuser | BooleanField | 管理者権限を持つか |
last_login | DateTimeField | 最終ログイン日時 |
date_joined | DateTimeField | ユーザー登録日時 |
これでも問題はないようですが、後日変更を入れたくなるケースが想定されるのでカスタムユーザーモデルをつくっておくのが推奨されているようです
カスタムユーザーモデル (AbstractUser)
デフォルトのUserモデルにFieldを追加して使いたい場合にはAbstractUserを選択します。自由度は下がりますが、足す方向で良いならこれを選択する方が簡単にできるようです
カスタムユーザーモデル (AbstractBaseUser)
最低限の機能だけ継承してFieldの設計からやりたい場合はAbstractBaseUserとPermissionsMixinを継承して作成します。機能は継承元クラスに既にありますので、Field設計だけやれば完成です
また、Field構造に応じてユーザーオブジェクトの作成方法も変更しなければならないので、BaseUserManagerをoverrideしてcreate_userメソッドとcreate_superuserメソッドも変更してやります
2-2. 作成手順
Djangoアプリを作成
アカウント情報を持つDjangoアプリを追加します
python manage.py startapp accounts
setting.pyに追加
AUTH_USER_MODELで認証用のユーザーモデルに指定します
INSTALLED_APPS = [
...
'accounts' # 追加
]
AUTH_USER_MODEL = "accounts.User" # 認証に使うカスタムユーザーモデルを指定
カスタムユーザーモデルの作成
今回はAbstractBaseUserとPermissionsMixinを使って作成します
UserクラスにField構成を定義して
UserManagerにオブジェクト作成時の処理を書いています
from django.db import models
from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser, PermissionsMixin)
from django.utils.translation import gettext_lazy as _
class UserManager(BaseUserManager):
def _create_user(self, email, account_id, password, **extra_fields):
email = self.normalize_email(email)
user = self.model(email=email, account_id=account_id, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, account_id, password=None, **extra_fields):
extra_fields.setdefault('is_active', True)
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(
email=email,
account_id=account_id,
password=password,
**extra_fields,
)
def create_superuser(self, email, account_id, password, **extra_fields):
extra_fields['is_active'] = True
extra_fields['is_staff'] = True
extra_fields['is_superuser'] = True
return self._create_user(
email=email,
account_id=account_id,
password=password,
**extra_fields,
)
class User(AbstractBaseUser, PermissionsMixin):
account_id = models.CharField(verbose_name=_("account_id"), unique=True, max_length=10)
email = models.EmailField(verbose_name=_("email"), unique=True)
first_name = models.CharField(verbose_name=_("first_name"), max_length=150, null=True, blank=False)
last_name = models.CharField(verbose_name=_("last_name"), max_length=150, null=True, blank=False)
birth_date = models.DateField(verbose_name=_("birth_date"), blank=True, null=True)
is_superuser = models.BooleanField(verbose_name=_("is_superuer"), default=False)
is_staff = models.BooleanField(verbose_name=_('staff status'), default=False)
is_active = models.BooleanField(verbose_name=_('active'), default=True)
created_at = models.DateTimeField(verbose_name=_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(verbose_name=_("updateded_at"), auto_now=True)
objects = UserManager()
USERNAME_FIELD = 'account_id' # ログイン時、ユーザー名の代わりにaccount_idを使用
REQUIRED_FIELDS = ['email'] # スーパーユーザー作成時にemailも設定する
def __str__(self):
return self.account_id
マイグレーションする
python manage.py makemigrations
python manage.py migrate
adminページで編集できるようにする
from django.contrib import admin
from django.contrib.auth.models import Group
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
pass
admin.site.unregister(Group) # Groupは要らないので非表示にする
superuserをつくる
admin編集用のsuperuserをつくります
ID, passwordを求められるので入力します
python manage.py createsuperuser
adminページでユーザーを登録
作成できているか確認しましょう
python manage.py runserver
SUPERUSERでログインしたままにすると後のログイン動作確認でSUPERUSERでログインしている扱いになってしまいますのでログアウトしておきます
3. 画面をつくる
3-1. ユーザー登録画面をつくる
formをつくる
from django.contrib.auth.forms import UserCreationForm
from .models import User
class SignUpForm(UserCreationForm):
class Meta:
model = User
fields = (
"account_id",
"email",
"first_name",
"last_name",
"birth_date",
)
viewをつくる
indexとsignupのViewをつくります
from django.contrib.auth import login, authenticate
from django.views.generic import TemplateView, CreateView
from django.urls import reverse_lazy
from .forms import SignUpForm
class IndexView(TemplateView):
""" ホームビュー """
template_name = "index.html"
class SignupView(CreateView):
""" ユーザー登録用ビュー """
form_class = SignUpForm # 作成した登録用フォームを設定
template_name = "accounts/signup.html"
success_url = reverse_lazy("accounts:index") # ユーザー作成後のリダイレクト先ページ
def form_valid(self, form):
# ユーザー作成後にそのままログイン状態にする設定
response = super().form_valid(form)
account_id = form.cleaned_data.get("account_id")
password = form.cleaned_data.get("password1")
user = authenticate(account_id=account_id, password=password)
login(self.request, user)
return response
templateをつくる
共通部分をbase.htmlとしてつくります
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Bootstrap5のCDNを設定 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
<title>Account</title>
</head>
<body>
<div class="container mx-auto text-center">
{% block title %}
{% endblock %}
{% block content %}
{% endblock %}
</div>
</body>
</html>
最初の画面をつくります
userが唐突に登場していますが、これは現在アクセスしているユーザーのユーザーオブジェクトです。django.contrib.auth.context_processors.authがCookieを使ってなんか上手いことやってくれるようです。user.is_authenticatedはDjangoのSUPERUSERでログインしていてもTrueになってしまうので、実用のコードではもう少し記載が必要だと思います。その場合、デフォルトのユーザーモデルにuser.first_nameとuser.last_nameがないので「None None」と表示されてしまいます
{% extends 'base.html' %}
{% block title %}
{% if user.is_authenticated %}
<div class="h1">MyPage</div>
{% else %}
<div class="h1">Main</div>
{% endif %}
{% endblock %}
{% block content %}
{% if user.is_authenticated %}
<div class="h2">Welcome {{ user.first_name }} {{ user.last_name }}</div>
{% else %}
<a href="{% url 'accounts:signup' %}" class="btn btn-primary">Signup</a>
{% endif %}
{% endblock %}
ユーザー登録画面をつくります
{% extends 'base.html' %}
{% block title %}
<div class="h1">Create account</div>
{% endblock %}
{% block content %}
<div>
<br>
<form method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label }}
{{ field }}
{{ field.errors }}
<br>
{% endfor %}
<div class="mt-3">
<button type="submit" class="btn btn-primary">Create</button>
<a href="{% url 'accounts:index' %}" class="btn btn-secondary">Back</a>
</div>
</form>
</div>
{% endblock %}
テンプレートのディレクトリをsettings.pyに追加
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # 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',
],
},
},
]
urls.pyにviewを登録
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("accounts.urls")) # accounts.urls.pyを読み込むための設定を追加
]
from django.urls import path
from . import views
app_name = "accounts"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path('signup/', views.SignupView.as_view(), name="signup"),
]
動かしてみる
python manage.py runserver
こんな感じで記入します

formsを利用して入力フォームをつくると入力バリデーションしてくれます

ユーザー登録すると以下のような表示になります

3-2. ログイン画面をつくる
サインアップして登録した情報でログインする画面をつくります
formをつくる
LoginFormを追加します
AuthenticationFormを継承してFormをつくると、modelは指定しなくてもデフォルトでsettings.pyのAUTH_USER_MODELで指定されているユーザーモデルを使って、ユーザー名とパスワードでログインするフォームが作成されます
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm # 追加
class SignUpForm(UserCreationForm):
class Meta:
model = User
fields = (
"account_id",
"email",
"first_name",
"last_name",
"birth_date",
)
class LoginFrom(AuthenticationForm):
pass
ログインIDとしてemailを使いたい場合は以下のようにバリデーション処理中に置き換えて扱えば出来るようです
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import get_user_model
from django import forms
User = get_user_model()
class EmailLoginForm(AuthenticationForm):
username = forms.EmailField(label="Email", widget=forms.EmailInput(attrs={"autofocus": True}))
def clean(self):
email = self.cleaned_data.get("username") # username フィールドを email として扱う
password = self.cleaned_data.get("password")
if email and password:
try:
user = User.objects.get(email=email)
self.user_cache = user
self.confirm_login_allowed(user)
except User.DoesNotExist:
raise forms.ValidationError("このメールアドレスのユーザーは存在しません。")
return self.cleaned_data
viewをつくる
LoginViewを追加します
from django.contrib.auth.views import LoginView as BaseLoginView
class LoginView(BaseLoginView):
form_class = LoginFrom
template_name = "accounts/login.html"
templateをつくる
ログイン画面のテンプレートを作成します
{% extends 'base.html' %}
{% block title %}
<div class="h1">Login</div>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label }}
{{ field }}
{{ field.errors }}
<br>
{% endfor %}
<div class="mt-3">
<button type="submit" class="btn btn-primary">Login</button>
<a href="{% url 'accounts:index' %}" class="btn btn-secondary">Back</a>
</div>
</form>
{% endblock %}
urlsにviewを登録
追加したloginページをルーティングに追加
from django.urls import path
from . import views
app_name = "accounts"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path('signup/', views.SignupView.as_view(), name="signup"),
path('login/', views.LoginView.as_view(), name="login"),
]
settings.pyにリダイレクト先を登録
ログイン後にリダイレクトされるURLを指定します
LOGIN_REDIRECT_URL = "accounts:index"
3-3. ログアウト画面をつくる
viewをつくる
LogoutViewを追加します
BaseLogoutViewを継承したViewクラスをつくっておいて、ボタンクリックから呼んでやればログアウト処理とログアウト後の画面表示をやってくれます
from django.contrib.auth.views import LogoutView as BaseLogoutView
class LogoutView(BaseLogoutView):
success_url = reverse_lazy("accounts:index")
templateにログアウトボタンを追加
ログアウト処理はCSRF対策の関係でPOSTメソッドで送るのが推奨されているようです。その為、BaseLogoutViewはGETリクエストを405エラーではじいてしまいますので、ちょっと面倒ですがPOSTメソッドで呼ぶようにしてやります
{% extends 'base.html' %}
{% block title %}
{% if user.is_authenticated %}
<div class="h1">MyPage</div>
{% else %}
<div class="h1">Main</div>
{% endif %}
{% endblock %}
{% block content %}
{% if user.is_authenticated %}
<div class="h2">Welcome {{ user.first_name }} {{ user.last_name }}</div>
<!-- ↓追加 -->
<form method="post" action="{% url 'accounts:logout' %}">
{% csrf_token %}
<button type="submit" class="btn btn-primary">ログアウト</button>
</form>
{% else %}
<a href="{% url 'accounts:signup' %}" class="btn btn-primary">Signup</a>
<a href="{% url 'accounts:login' %}" class="btn btn-primary">Login</a>
{% endif %}
{% endblock %}
urlsにviewを登録
追加したloginページをルーティングに追加
from django.urls import path
from . import views
app_name = "accounts"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path('signup/', views.SignupView.as_view(), name="signup"),
path('login/', views.LoginView.as_view(), name="login"),
path('logout/', views.LogoutView.as_view(), name="logout"),
]
settings.pyにリダイレクト先を登録
ログアウト後にリダイレクトされるURLを指定します
LOGOUT_REDIRECT_URL = "accounts:login"
動かしてみる
これでログイン、ログアウト機能が追加できた筈です
python manage.py runserver
まとめ
めちゃめちゃ簡単に実装できます
Djangoすごい
レッツトライ