このページでは、Django における管理画面(admin
)のカスタマイズ方法について解説していきます。管理画面の中でも、特に「ユーザー管理画面」のカスタマイズについて解説していきます。
このカスタマイズを行うことで、アプリ開発者が独自で作成したユーザー(カスタムユーザー)に合わせたユーザー管理画面を実現することができるようになります。
管理画面とは、通常下記の URL からアクセスできるページのことになります(設定変更等により管理画面の URL は変更することも可能です)。
http://localhost:8000/admin/
実際の画面としては下図のようなものになり、この画面からユーザーを管理することが可能です。この、特にユーザーを管理する画面のことを「ユーザー管理画面」と呼ばせていただいています。
このユーザー管理画面では、例えばユーザー一覧リストを表示したり、
さらにはユーザーの追加を行ったりすることが可能です。他にもユーザーの情報の変更や削除なども行えます。
また、当然と言えば当然ですが、このユーザー管理画面で入力したパスワードは下の図のようにマスクされており、他の人からは見えないようになっています。
また、入力されたパスワードに対しては妥当性の検証が行われ、パスワードとして相応しくない場合(例えば文字数が少ない・名前等の他の情報と似ている場合など)にはパスワードの再入力が促されるようになっており、セキュリティ的にも強化されています。
ただし、ここで示したユーザー管理画面は、認証に利用するユーザーモデル(ユーザー管理モデル)が User
である場合の画面となります(User
は Django に標準で用意されているユーザー管理モデルとなります)。
その一方で、下記ページでも解説しているとおり、Django ではカスタムユーザーを作成することができ、それを User
の代わりに利用することも可能です。


そして、これは上記ページでも解説していますが、アプリフォルダ内の admin.py
で下記を実行することにより、自身で作成したカスタムユーザーのインスタンス(つまり各ユーザー)をユーザー管理画面で管理することも可能です。
from django.contrib import admin
admin.site.register(カスタムユーザーのモデル名)
ですが、この場合、別途ユーザー管理画面のカスタマイズを行わないと、ユーザー一覧に表示されるフィールドが不足していたり、ユーザーを追加する場合に不要なフィールドの入力受付も行われて冗長だったりします。
さらに、ユーザー管理画面で入力したパスワードはそのまま表示されてしまいます。
また、パスワードの妥当性検証も行われないため、極端に短い文字列やユーザー名等の他のフィールドと似ている文字列を設定した場合でもパスワードの登録を行うことが可能になってしまっており、セキュリティ的にイマイチです。
こんな感じで、カスタムユーザーを利用している場合、User
を利用している時に比較して管理画面が不便なものになってしまっています。
ここからは、この管理画面が不便なものになってしまっている理由、すなわち、User
を利用している場合とカスタムユーザーを利用している場合とでユーザー管理画面が大きく異なる理由と、User
を利用しているときと同様にユーザー管理画面での利便性を向上させるためのカスタマイズ方法について解説していきたいと思います。
Contents
カスタムユーザー利用時に管理画面が不便になる理由
では、まずはカスタムユーザーを利用している場合と User
を利用している場合とでユーザー管理画面に違いが生じる理由について解説していきます。
この根本的な理由は「ユーザー管理画面作成時に動作するクラスが違うため」になります。
ModelAdmin
と UserAdmin
の関係
まず、カスタムユーザーを利用している場合、ユーザー管理画面は ModelAdmin
というクラスが動作することで作成されます。
それに対し、User
を利用している場合、ユーザー管理画面は UserAdmin
というクラスが動作することで作成されます。
このように、ユーザー管理画面を作成する際に動作するクラスが異なります。
そして、この2つのクラスには下の図のような継承関係があります。つまり、UserAdmin
は ModelAdmin
を継承して作成されたクラスとなります。
ModelAdmin
と UserAdmin
の違い
さらに、UserAdmin
は ModelAdmin
を「User
管理用にカスタマイズしたクラス」となります。User
を管理するのですから、パスワードなどは当然他の人に見られないようにマスクして表示されるようなカスタマイズが行われています。
また、ユーザー一覧リストにおいては User
にとって重要なフィールドが表示され、ユーザー追加フォームにおいては User
を管理するために必須となる情報のフィールドに対してのみ入力受付が行われるようにカスタマイズされています。
それに対し、UserAdmin
の継承元の ModelAdmin
は User
に特化したものではなく、一般的なモデルを管理するためのクラスとなっています。
一般的なモデルにおいてはパスワード入力が不要なものも多く、パスワード入力エントリーも通常のエントリーとして扱われるのでマスクされません。
また、ユーザー一覧リストにおいてはモデルのインスタンスを識別するための情報のみが、さらにユーザー追加フォームにおいてはモデルに用意されたすべてのフィールドの入力受付が行われるようになっています。
このような違いがあるため、カスタムユーザーを利用している場合と User
を利用している場合とでユーザー管理画面の違いが生じます。
そして、カスタムユーザーに関しては一般的なモデルと同様の管理が行われるようにユーザー管理画面が作成されるため、ユーザー管理を行う上では不便な管理画面になってしまっています。
カスタムユーザーに応じた管理画面を作成する方法
ここまで聞くと、カスタムユーザー利用時もユーザー管理画面を UserAdmin
から作成させるようにすれば良いのでは?と感じる方も多いと思います。
方向性は合っています。
ただ、前述の通り、UserAdmin
は User
モデルのインスタンスを管理するためのクラスであり、そのままではカスタムユーザーのインスタンスを管理するのが困難です。
カスタムユーザーは開発者が独自で作成するモデルであるため、User
モデルに存在しないフィールドを持っていたり、User
モデルの持つフィールドを持っていなかったりします。
それに対し、UserAdmin
は管理するモデルが User
であることを前提に作られているため、User
モデルの持つフィールドに合わせて表示や入力受付が行われています。
そのため、UserAdmin
でカスタムユーザーを管理しようとすると、存在しないフィールドを表示しようとして例外が発生する、設定が必須のフィールドの入力受付が行われなくて情報が足りなくて例外が発生する、などといった現象が発生することになります。
例えばカスタムユーザーに username
フィールドを持たせなかった場合でも、ユーザー一覧リストで各ユーザーの username
を表示しようとするため、そこで例外が発生することになります。
上記のような理由から、UserAdmin
をそのまま利用してカスタムユーザーを管理するのは難しいです。
ですので、単に UserAdmin
をそのまま利用するのではなく、UserAdmin
を継承するクラスを定義し、そのクラスをカスタムユーザー向けにカスタマイズすることが必要になります。
もしくは、UserAdmin
ではなく ModelAdmin
を継承するクラスを定義し、そのクラスをカスタムユーザー管理用に作成していくのでも良いと思います。
前者の場合、 UserAdmin
を継承するためユーザーを管理する基本的な機能も継承することができ、必要な変更は過不足しているフィールドの対応くらいに抑えることができます。
それに対し、後者の場合は、一般的なモデルの管理からユーザー管理するためのクラスに仕立てていくことが必要になるため必要な変更が多くなります。例えばパスワードをマスクするなどの処理も実装が必要になります。
今回は、前者のやり方、すなわち、UserAdmin
を継承してクラスを定義し、そのクラスをカスタムユーザーに合わせてカスタマイズすることで、カスタムユーザーに応じた管理画面の作成を行なっていきたいと思います。
カスタムユーザーに応じた管理画面の実現
それでは、カスタムユーザーに応じた管理画面の実現手順について解説していきます。
前述の通り、UserAdmin
を継承してクラスを定義し、そのクラスのカスタマイズを行うことでカスタムユーザーに応じた管理画面を実現していきます。この定義するクラス名は CustomUserAdmin
としたいと思います。
また、実例があった方が良いと思うので、下記の CustomUser
を管理することを想定し、この CustomUser
に応じた管理画面を作成する実例も踏まえながら解説をしていきたいと思います(UserManager
も一応用意しています)。
from django.db import models
from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.contrib.auth.hashers import make_password
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given email and password.
"""
if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
# Lookup the real model class from the global app registry so this
# manager method can be used in migrations. This is fine because
# managers are by definition working on the real model.
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
def with_perm(
self, perm, is_active=True, include_superusers=True, backend=None, obj=None
):
if backend is None:
backends = auth._get_backends(return_tuples=True)
if len(backends) == 1:
backend, _ = backends[0]
else:
raise ValueError(
"You have multiple authentication backends configured and "
"therefore must provide the `backend` argument."
)
elif not isinstance(backend, str):
raise TypeError(
"backend must be a dotted import path string (got %r)." % backend
)
else:
backend = auth.load_backend(backend)
if hasattr(backend, "with_perm"):
return backend.with_perm(
perm,
is_active=is_active,
include_superusers=include_superusers,
obj=obj,
)
return self.none()
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
height = models.FloatField()
weight = models.FloatField()
objects = UserManager()
EMAIL_FIELD = "email"
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["height", "weight"]
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
#abstract = True
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
この CustomUser
は AbstractBaseUser
と PermissionsMixin
を継承して作成したカスタムユーザーモデルであり、AbstractBaseUser
と PermissionsMixin
を継承してカスタムユーザーを作成する手順は下記ページで解説していますので、まずカスタムユーザーの作り方について知りたい方は下記ページをご参照いただければと思います。

少し説明を加えておくと、上記の CustomUser
では、User
に比べて下記のフィールドを削除しています。
username
first_name
last_name
is_active
date_joined
さらに、User
に比べて下記のフィールドを追加しています。そして、これらのフィールドは必須フィールドとして設定しています(必須フィールドなので一応 REQUIRED_FIELDS
にもこれらのフィールドを追加しています)。
height
weight
前述の通り、UserAdmin
は User
を管理することを前提としたクラスとなっていますので、UserAdmin
を継承する CustomUserAdmin
を “上記のフィールドの違いを考慮しながら” カスタマイズしていくという点が、カスタムユーザーに応じた管理画面の実現を行う上でのポイントになります。
スポンサーリンク
CustomUserAdmin
の定義
では、まずは CustomUserAdmin
を定義していきたいと思います。
定義先のファイルはアプリフォルダ内の admin.py
となります。ひとまず、単に UserAdmin
を継承するだけのクラスとして、下記のように CustomUserAdmin
を定義したいと思います。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
pass
CustomUser = get_user_model()
admin.site.register(CustomUser)
前述の通り、CustomUserAdmin
は UserAdmin
を継承するのみで作成しているため、UserAdmin
同等のクラスになります。
ですので、このままだと User
を管理することを前提としたクラスになってしまっていますので、User
ではなく CustomUser
を管理できるようにカスタマイズしていく必要があります。
CustomUserAdmin
の管理画面への登録
カスタマイズを行う前に、まずは CustomUserAdmin
を管理画面で利用されるように、管理画面への登録を行いたいと思います。
この登録は、下記のように admin.site.register
の第2引数に CustomUserAdmin
を指定することで実現することができます。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
pass
CustomUser = get_user_model()
admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register
の第1引数では、管理画面で管理を行うユーザーモデルの指定を行います。これにより、管理画面で第1引数に指定したモデルのインスタンスを管理することができるようになります。
また、admin.site.register
の第2引数では、管理画面表示時に使用するクラスを指定します。これにより、管理画面が第2引数に指定したクラスに応じた画面に変化します(指定をしなかった場合は ModelAdmin
がデフォルトで指定されるようになっています)。
ですので、上記のように admin.site.register
を実行することで、CustomUserAdmin
クラスによって作成される管理画面で CustomUser
のインスタンスを管理することができるようになります。
ただし、前述の通り、現状の CustomUserAdmin
は User
のインスタンスを管理することを前提としたクラスとなっています。
そのため、admin.py
を上記のように変更して Django の開発用ウェブサーバーを起動した場合、User
に対して CustomUser
のフィールドが不足しているので例外が発生することになります。
具体的に発生する例外は下記のようなものになります。
ERRORS: <class 'login_app.admin.CustomUserAdmin'>: (admin.E033) The value of 'ordering[0]' refers to 'username', which is not a field of 'login_app.CustomUser'. <class 'login_app.admin.CustomUserAdmin'>: (admin.E108) The value of 'list_display[0]' refers to 'username', which is not a callable, an attribute of 'CustomUserAdmin', or an attribute or method on 'login_app.CustomUser'. <class 'login_app.admin.CustomUserAdmin'>: (admin.E108) The value of 'list_display[2]' refers to 'first_name', which is not a callable, an attribute of 'CustomUserAdmin', or an attribute or method on 'login_app.CustomUser'. <class 'login_app.admin.CustomUserAdmin'>: (admin.E108) The value of 'list_display[3]' refers to 'last_name', which is not a callable, an attribute of 'CustomUserAdmin', or an attribute or method on 'login_app.CustomUser'. <class 'login_app.admin.CustomUserAdmin'>: (admin.E116) The value of 'list_filter[2]' refers to 'is_active', which does not refer to a Field.
ご覧の通り、User
と比較して CustomUser
に足りないフィールドに対して例外が発生していルことを確認していただけると思います。
現状の CustomUserAdmin
は User
を管理することを前提としたクラスとなっているため、上記のような例外が発生してしまいますが、CustomUserAdmin
をカスタマイズして例外が発生する原因となっているフィールドを利用しないようにすることで、上記の例外を解消することができます。
CustomUserAdmin
のカスタマイズ
ということで、次は CustomUserAdmin
で CustomUser
に存在するフィールドのみを利用するようにカスタマイズしていきたいと思います。
まずは、現状の把握の意味も込めて、UserAdmin
がどのようなフィールドを利用しているのかを調べたいと思います。これは、UserAdmin
の定義を表示してやることで簡単に調べることができます。
VScode 限定の話になりますが、クラスの定義の調べ方については下記ページでも紹介しているので、こちらを参考にしていただければと思います。

とりあえず Django 4.0.5 の場合、UserAdmin
の定義を利用するフィールドのみに焦点を当てて抜粋すると下記のようになっています。
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
fieldsets = (
(None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2"),
},
),
)
list_display = ("username", "email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
search_fields = ("username", "first_name", "last_name", "email")
ordering = ("username",)
上記で定義されている各クラス変数の簡単な意味合いについて解説しておきます。
fieldsets
fieldsets
はユーザーの詳細画面で表示するフィールド、および、ユーザーの情報変更を行う際に入力受付を行うフィールドを指定するクラス変数になります。
add_fieldsets
add_fieldsets
はユーザー追加フォームでユーザーからの入力を受け付けるフィールドを指定するクラス変数になります。
list_display
list_display
はユーザー一覧リストに表示するフィールドを指定するクラス変数になります。
list_filter
list_filter
は、ユーザー一覧リストに対するフィルタリング可能なフィールドを指定するクラス変数になります。
例えば、このフィルタリングを利用して is_superuser
フィールドが True
のユーザーのみをユーザー一覧リストに表示するようなことが可能です。
上の図では表示されていませんが、list_filter
に "groups"
を指定しておけば、グループが1つ以上存在する場合にフィルターの項目に By groups
が追加されます。
search_fields
search_fields
はユーザー検索を行う際に、検索対象に含めるフィールドを指定するクラス変数になります。
例えば上の図のユーザーの例で考えると、serarch_fields
に "email"
を指定している場合、abc
で検索を行うと email
に abc
が含まれるユーザー Jiro
がヒットすることになります。それに対し、serarch_fields
に "email"
を指定していない場合、email
は検索対象として扱われませんので、abc
で検索してもユーザーは一人もヒットしないことになります。
ordering
ordering
はユーザー一覧リスト初期表示時のユーザー表示順を決定するフィールドを指定するクラス変数になります。
例えば ordering
に指定するタプルの第1要素に "email"
を指定すれば、email
の文字列に対して昇順にユーザーが並んだ状態でユーザー一覧リストが表示されるようになります。
filter_horizontal
この filter_horizontal
については正直私も余り理解できていないのですが、おそらくユーザー追加時等に下図のように選択式での設定を行うことができるフィールドを指定するクラス変数になると思います。
今回は filter_horizontal
の変更は行いませんが、モデルに ManyToManyField
を持たせるような場合、filter_horizontal
にそのフィールドを指定して選択式でフィールドの設定を行えるようにしてやることでユーザー管理の利便性が上がります。
今回はフィールドを指定するクラス変数のみに焦点を当てて解説していますが、他にも管理画面で使用するテンプレートなどをクラス変数の定義によって指定することも可能です
例えば add_form_template
を定義することによりユーザー追加フォーム表示時のテンプレートを変更するようなことも可能です
他にどのような指定を行うことができるのかについては UserAdmin
クラスの定義をご確認いただければと思います
削除したフィールドを利用しないようにカスタマイズ
UserAdmin
の定義をご覧いただければ分かる通り、各クラス変数において CustomUser
に存在しない下記のフィールドが指定されています。
username
first_name
last_name
is_active
date_joined
現状の CustomUserAdmin
は UserAdmin
を継承するのみで作成しているため、UserAdmin
同様に、CustomUserAdmin
でも上記のフィールドの表示や上記のフィールドの入力受付が行われるようになっていることになります。
そのため、管理画面で CustomUser
を管理できるようにするには、CustomUserAdmin
で上記のフィールドを指定しないように CustomUserAdmin
をカスタマイズする必要があります。
やり方は単純で、先ほど示した UserAdmin
の各クラスの定義を CustomUserAdmin
のクラス変数として定義し、さらにそのクラス変数に指定する値やタプル等から上記で挙げたフィールドの削除を行います。
CustomUserAdmin
のクラス変数の定義が優先して使用されるため、これによって上記のフィールドが管理画面作成時に利用されないようになります。
具体的な手順としては、まず、CustomUserAdmin
に対し、UserAdmin
でフィールドの指定を行なっている各クラス変数の定義をコピペしてきます。そうすると、admin.py
は下記のようになると思います。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2"),
},
),
)
list_display = ("username", "email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
search_fields = ("username", "first_name", "last_name", "email")
ordering = ("username",)
CustomUser = get_user_model()
admin.site.register(CustomUser)
さらに、上記の各クラス変数において、CustomUser
に存在しないフィールドを削除 or 他のフィールドへの置き換えを行います。
とりあえず例外が発生しないようにするのであれば、admin.py
を下記のように変更してやれば良いです。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
(
_("Permissions"),
{
"fields": (
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login",)}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
list_display = ("email", "is_staff")
list_filter = ("is_staff", "is_superuser", "groups")
search_fields = ("email", "email")
ordering = ("email",)
filter_horizontal = (
"groups",
"user_permissions",
)
CustomUser = get_user_model()
admin.site.register(CustomUser, CustomUserAdmin)
各クラス変数の変更によって管理画面がどのように変化するのかについては、CustomUserAdmin のカスタマイズ の冒頭で説明した各クラス変数の説明を参照していただければと思います。
例えばですが、上記のように add_fieldsets
を変更することにより、管理画面でユーザーを追加する際の入力フォームは次の図のように変化します。
このように、add_fieldsets
に指定したフィールドの入力受付が行えるようユーザー追加フォームが変化していることが確認できると思います(最初の入力エントリーが username
ではなく email
に変わっている)。
また、上図のユーザー追加フォームにおいては、パスワードがマスクされて他の人から見られないようになっています。このパスワードのマスクに関しては、前述の通り UserAdmin
を継承することで実現される機能となります。
ただ、ページの冒頭に下記のような文言があり、ここに username
という文言が残っているので気持ち悪いですね…。今回は説明は省略させていただきますが、このようなページの表示文字列等を変更する場合は使用するテンプレートの変更が必要になります。
First, enter a username and password. Then, you’ll be able to edit more user options.
追加したフィールドを利用するように変更
さて、前述のように CustomUserAdmin
を変更することで、管理画面表示時に CustomUser
に存在しないフィールドが利用されないようになったことになります。
ただ、まだ問題点があります。
これは実際にユーザーの追加を行なってみれば分かるのですが、現状の CustomUserAdmin
を利用して管理画面からユーザーの追加を行うと、下記のような例外が発生することになります。
django.db.utils.IntegrityError: NOT NULL constraint failed: login_app_customuser.height
なんとなくこの例外から現象を推測していただけると思うのですが、現状の CustomUserAdmin
ではユーザー追加時に height
、さらには weight
の入力受付が行われないため、これらの設定を行うことができずに上記のような例外が発生してしまうことになります。
もう少し詳しく説明しておくと、現状の CustomUserAdmin
の add_fieldsets
では "email"
・"password1"
・"password2"
のみが指定されているため、ユーザー追加フォームにおいては、この3つのフィールドの入力受付のみが行われるようになっています。
そして、ユーザー追加時には、これらの入力値が CustomUser
のインスタンスに設定され、そのインスタンスの情報がレコードとしてデータベースに保存されるようになっています(実際には password1
と password2
ではなく、password
のフィールドに値が設定されることになります)。
ただし、CustomUser
では User
に比較して height
と weight
のフィールドを追加しており、さらにこれらのフィールドは設定必須項目として扱われるようになっています。
そのため、height
と weight
のフィールドが設定されていないレコードをデータベースに保存しようとすると、設定必須項目が設定されていないことを理由に上記のような例外が発生することになります。
この例外は、ユーザー追加フォームで height
と weight
の入力受付を行うようにすれば解決することができますので、この方法で例外を解決していきたいと思います(他にも height
と weight
を “設定任意項目” に設定して解決するようなこともできます)。
具体的には、ユーザー追加フォームで入力受付を行うフィールドの指定を行う add_fieldsets
に "height"
と "weight"
の指定を追加してやれば良いです。
また、add_fieldsets
に対してだけでなく、height
と weight
のような CustomUser
に独自に追加したフィールドを他のクラス変数に指定することもできます。そして、これによって管理画面でのユーザー管理の利便性を向上させることができます。
例えば下記のように admin.py
を変更してやれば、ユーザー追加時に height
と weight
の入力受付が行われるようになり、ユーザー一覧リスト等にも height
と weight
が表示されるようになります。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
(_("Personal info"), {"fields": ("height", "weight")}),
(
_("Permissions"),
{
"fields": (
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login",)}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "height", "weight", "password1", "password2"),
},
),
)
list_display = ("email", "height", "weight", "is_staff")
list_filter = ("is_staff", "is_superuser", "groups")
search_fields = ("email", "email")
ordering = ("height",)
filter_horizontal = (
"groups",
"user_permissions",
)
CustomUser = get_user_model()
admin.site.register(CustomUser, CustomUserAdmin)
例えば、上記の CustomUserAdmin
を利用して表示されるユーザー一覧リストは次の図のようになります。
height
と weight
が表示されていることが確認できると思いますし、ordering = ("height",)
としているため、height
に対して昇順にユーザーが並んで表示されていることも確認できると思います。
管理画面のカスタマイズにおけるポイント
こんな感じで、管理画面でカスタムユーザーを管理するためには、作成したカスタムユーザーに合わせてユーザー管理画面のカスタマイズを行うことが必要になります。
重要なのは、ここまで説明してきたように、カスタムユーザーの持つフィールドに合わせてユーザー管理画面で利用するフィールドを設定することになります。
ここが上手く実現できていないとユーザー管理画面で例外が発生し、上手くユーザーを管理することができなくなるので注意してください。
特に下記の2点については最低限実現できるように、ユーザー管理画面のカスタマイズを行うようにしてください。
- カスタムユーザーが持たないフィールドはユーザー管理画面からも利用しない
- カスタムユーザーに設定必須のフィールドはユーザー管理画面から設定できるようにする
スポンサーリンク
ユーザー管理画面のカスタマイズ例
以上がユーザー管理画面のカスタマイズ手順の解説になります。
最後に、ここまでの復習の意味を込めて、ユーザー管理画面でカスタムユーザーを管理するまでの手順および実装例をまとめて示しておきたいと思います。
特にここまでは admin.py
に焦点を当てて解説をしてきましたが、ここでは他のファイルの変更例も含めてユーザー管理画面でカスタマイズを管理するために最低限必要な実装を紹介していきます。
ただし、ここでは詳細な説明は省略させていただきますので、特にカスタムユーザーの作り方やアプリでのカスタムユーザーの利用の仕方については下記のページを必要に応じて参考にしていただければと思います。


プロジェクトとアプリの作成
では、まずはプロジェクトとアプリを作成していきたいと思います。
プロジェクト名は my_project
、アプリ名は accounts
にしたいと思います。
最初に、下記コマンドを実行してプロジェクト my_project
を作成します。
% django-admin startproject my_project
続いて、上記コマンドで作成される my_project
フォルダの中に移動し、さらに startapp
を実行してアプリ accounts
を作成します。
% cd my_project % python manage.py startapp accounts
次に、my_project
フォルダの中にある settings.py
内の INSTALLED_APPS
を下記のように変更し、プロジェクトに対してアプリを登録します。
# Application definition
INSTALLED_APPS = [
'accounts', # 追加
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
カスタムユーザーの作成
続いてカスタムユーザーを作成していきたいと思います。
今回は、カスタムユーザーとして カスタムユーザーに応じた管理画面の実現 で示した CustomUser
をそのまま利用したいと思います。
ということで、accounts
フォルダの中にある models.py
を開き、下記のように変更してください。
from django.db import models
from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.contrib.auth.hashers import make_password
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given email and password.
"""
if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
# Lookup the real model class from the global app registry so this
# manager method can be used in migrations. This is fine because
# managers are by definition working on the real model.
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
def with_perm(
self, perm, is_active=True, include_superusers=True, backend=None, obj=None
):
if backend is None:
backends = auth._get_backends(return_tuples=True)
if len(backends) == 1:
backend, _ = backends[0]
else:
raise ValueError(
"You have multiple authentication backends configured and "
"therefore must provide the `backend` argument."
)
elif not isinstance(backend, str):
raise TypeError(
"backend must be a dotted import path string (got %r)." % backend
)
else:
backend = auth.load_backend(backend)
if hasattr(backend, "with_perm"):
return backend.with_perm(
perm,
is_active=is_active,
include_superusers=include_superusers,
obj=obj,
)
return self.none()
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
height = models.FloatField()
weight = models.FloatField()
objects = UserManager()
EMAIL_FIELD = "email"
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["height", "weight"]
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
#abstract = True
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
スポンサーリンク
AUTH_USER_MODEL
の設定
カスタムユーザーが作成できましたので、次はアプリで認証に利用するモデルを先ほど作成した CustomUser
に設定していきます。
そのために、settings.py
の最後に下記を追記します。
AUTH_USER_MODEL = 'accounts.CustomUser'
マイグレーションの実行
続いてマイグレーションを実行しましょう!
startproject
で作成された my_project
フォルダ内で下記の2つのコマンドを実行してみてください。おそらくエラーが発生することなく正常にマイグレーションが実行できるはずです。
% python manage.py makemigrations % python manage.py migrate
このマイグレーションによってデータベースに accounts.customuser
というテーブルが作成されることになります。
スーパーユーザーの追加
ここから本題の管理画面に関する変更を行なっていくのですが、管理画面にログインするためにはスーパーユーザーが必要です。
ということで、まずはスーパーユーザーを追加するために、startproject
で作成された my_project
フォルダ内で下記コマンドを実行してみてください。
% python manage.py createsuperuser
上記コマンドを実行すれば、作成するスーパーユーザーのメールアドレス・身長・体重・パスワード(確認用も含めて2回)の入力受付が順次行われますので作成したスーパーユーザーに合わせて入力を行なっていってください。
% python manage.py createsuperuser Email address: hanako@yamada.jp Height: 167.2 Weight: 56.2 Password: Password (again): Superuser created successfully.
この入力によって、スーパーユーザーが作成されるはずです。
管理画面の設定後、このスーパーユーザーで管理画面へのログインを行うため、上記で入力したメールアドレスとパスワードはメモしておいてください。
スポンサーリンク
管理画面の設定
最後に管理画面の設定を行ない、models.py
に作成したカスタムユーザーに合わせたユーザー管理画面を実現していきます。
この設定に関しても、カスタムユーザーに応じた管理画面の実現 で紹介した内容と同じ設定を行うことにしたいと思います。
要は、UserAdmin
を継承するクラスを定義し、そのクラスを CustomUser
の持たないフィールドを利用しないように、さらに CustomUser
に追加したフィールドを利用しないようにカスタマイズを行います。
これは、結局は accounts
フォルダの下の admin.py
を下記のように変更することで実現できます。
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("email", "password")}),
(_("Personal info"), {"fields": ("height", "weight")}),
(
_("Permissions"),
{
"fields": (
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login",)}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "height", "weight", "password1", "password2"),
},
),
)
list_display = ("email", "height", "weight", "is_staff")
list_filter = ("is_staff", "is_superuser", "groups")
search_fields = ("email", "email")
ordering = ("height",)
filter_horizontal = (
"groups",
"user_permissions",
)
CustomUser = get_user_model()
admin.site.register(CustomUser, CustomUserAdmin)
動作確認
変更は以上になります。最後に動作確認を行なっておきましょう!
管理画面にアクセスするためには、まずは下記コマンドで Django の開発用ウェブサーバーを起動しておく必要があります。
% python manage.py runserver
Django の開発用ウェブサーバー起動後、ウェブブラウザで下記 URL にアクセスすれば、管理画面へのログインフォームが表示されるはずです。ここには、先ほど作成したスーパーユーザーのメールアドレスとパスワードを入力してください。
http://localhost:8000/admin/
ログインに成功すれば、あとはいつも通りの感覚でユーザーの管理を行なってみてください。
例えば管理画面のトップページにおける Users
リンクをクリックすればユーザー一覧リストが表示されます。まだユーザーは一人だと思いますが、height
や weight
といった CustomUser
独自のフィールドの情報が表示されていることが確認できると思います。
また、ユーザー一覧リスト表示ページにおける右上の ADD USER +
ボタンをクリックすればユーザー追加フォームが表示され、ここでも height
や weight
といった CustomUser
独自のフィールドの入力が可能であることを確認できると思います。
このような動作結果より、admin.py
の変更によって、管理するユーザーのモデルに合わせたユーザー管理画面のカスタマイズを行うことができることを確認していただけるのではないかと思います。
まとめ
このページでは、Django におけるユーザー管理画面(admin
)のカスタマイズ方法について解説しました!
Django ではユーザー管理画面をカスタマイズすることが可能であり、これによってカスタムユーザー(独自に作成したユーザー)を管理画面から管理することができるようになります。
具体的には、UserAdmin
を継承するクラスを定義し、そのクラスをカスタムユーザーに合わせてカスタマイズすることによって管理画面からのカスタムユーザーの管理を実現することができます。
あくまでも UserAdmin
は User
を管理するためのクラスであり、そのまま使用すると User
の持つフィールドに合わせて管理画面が作成されることになるので注意してください。User
の持つフィールドをカスタムユーザーが持たないような場合、例外が発生する恐れがあります。
また、デフォルトでもユーザーを管理することは可能ではあるのですが、パスワードがそのまま表示されたり不要なフィールドの入力受付が行われたりして管理画面としてはイマイチなので、今回紹介した手順で管理画面をカスタマイズしてやった方が良いと思います。
カスタムユーザーを利用することで実現可能なアプリの幅広がるのですが、今回説明した管理画面等の設定も必要になるので注意してください!