(最終更新月:2021年12月)
「関連のある2つのモデルクラスを同一ページで編集できるようにしたい!」
「Adminページをカスタマイズしたい!」
というDjangoユーザーの方向けの記事となります
当記事を通じて、
- ユーザーモデルクラスのための「UserAdmin」でオリジナルのフォームを適用する方法
- 編集フォームのカスタマイズ実例
について解説します
UserAdminにカスタマイズしたフォームを適用する
ユーザーモデルクラスのカスタマイズするためのUserAdminでは、以下の変数に入れることで独自のフォームを使用できます。
- add_form変数:新規登録フォーム
- form変数:編集ページ
また、フィールドの表示のみカスタマイズしたい場合は以下の変数にそのラベルやフィールドを入力すればOKです。
- add_fieldsets変数:新規登録のためのフィールド
- fieldsets変数:編集のためのフィールド
変数への入れ方としてはこちらに詳しく書いています。
コードの中では下記のようになります
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
class UserAdmin(BaseUserAdmin):
#add_form = ここに新規登録フォームを格納しよう!
#form = ここに編集フォームを格納しよう
##...割愛...
#新規登録フォームの新たなフィールドはここで追加します
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')}
),
)
#編集フォームの新たなフィールドはここで追加します
fieldsets = (
(None, {'fields': ('email', 'password')}),
#("見出し", {"fields": (フィールド名1, ...)}),
('Permissions', {'fields': ('staff','admin',)}),
)
admin.site.register(User, UserAdmin)
今回の変更については以下のとおり。
Userクラスとその拡張したクラス「Profile」を同ページに表示し編集できるようにします。
【変更前】
【変更後】
新規登録ページはデフォルトの通りとし、編集ページでのみ変更をしていきます。
【編集フォーム】accounts > forms.py
フォームを作成していきます
from django import forms
from django.forms.fields import DateField
from django.utils.translation import gettext, gettext_lazy as _
from django.contrib.auth.forms import UserChangeForm
from .models import User, Profile, GENDER_CHOICE
#GENDER_CHOICE = [(None, "--"), ("m", "男性"), ("f", "女性")]はmodels.pyで定義してます
class CustomAdminChangeForm(UserChangeForm):
#Profileクラスのフィールドを追記します
username = forms.CharField(max_length=100)
department = forms.CharField(max_length=100, required=False)
phone_number = forms.IntegerField(required=False)
gender = forms.ChoiceField(choices=GENDER_CHOICE, required=False)
birthday = DateField(required=False)
class Meta:
model = User
fields =('email', 'password', 'active', 'admin')
#Profileが存在する場合は、初期値にデータを格納する
def __init__(self, *args, **kwargs):
user_obj = kwargs["instance"]
if hasattr(user_obj, "profile"):
profile_obj = user_obj.profile
self.base_fields["username"].initial = profile_obj.username
self.base_fields["department"].initial = profile_obj.department
self.base_fields["phone_number"].initial = profile_obj.phone_number
self.base_fields["gender"].initial = profile_obj.gender
self.base_fields["birthday"].initial = profile_obj.birthday
super().__init__(*args, **kwargs)
#保存機能の定義
def save(self, commit=True):
user_obj = super().save(commit=False)
username = self.cleaned_data.get("username")
department = self.cleaned_data.get("department")
phone_number = self.cleaned_data.get("phone_number")
gender = self.cleaned_data.get("gender")
birthday = self.cleaned_data.get("birthday")
if hasattr(user_obj, "profile"):
profile_obj = user_obj.profile
else:
profile_obj = Profile(user=user_obj)
if username is not None:
profile_obj.username = username
if department is not None:
profile_obj.department = department
if phone_number is not None:
profile_obj.phone_number = phone_number
if gender is not None:
profile_obj.gender=gender
if birthday is not None:
profile_obj.birthday = birthday
profile_obj.save()
if commit:
user_obj.save()
return user_obj
ポイントは下記のとおりです
- 継承しているUserChangeFormは、本来はユーザーモデルクラスのみを編集するためのフォーム
- Meta内ではUserモデルクラスを指定すること
- Profileクラスのフィールドを追記します
- __init__メソッドで、データが存在する場合は.initial(初期値)として設定してます
- saveメソッド内で、値を取り出し保存する
【カスタムフォームを適用する】accounts > admin.py
フォームが完成したら以下の流れで完成します。
- 「form」変数へ作成したフォームクラスを格納
- 「fieldsets」で新たな項目”プロフィール”を作成
まずは抜粋したコードをご覧ください
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User, Profile
from .forms import CustomAdminChangeForm
class UserAdmin(BaseUserAdmin):
form = CustomAdminChangeForm
#...
fieldsets = (
#...,
('プロフィール', {'fields': (
'username',
'department',
'phone_number',
'gender',
'birthday',
)}),
#...,
)
admin.site.register(User, UserAdmin)
コード全体はこの通り。
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User, Profile
from .forms import CustomAdminChangeForm
class UserAdmin(BaseUserAdmin):
form = CustomAdminChangeForm
list_display = (
"email",
"active",
"staff",
"admin",
)
list_filter = (
"admin",
"active",
)
filter_horizontal = ()
ordering = ("email",)
search_fields = ('email',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('プロフィール', {'fields': (
'username',
'department',
'phone_number',
'gender',
'birthday',
)}),
('Permissions', {'fields': ('staff','admin',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')}
),
)
admin.site.register(User, UserAdmin)
#Profileクラスは不要になったのでコメントアウト
# admin.site.register(Profile)
まとめ
当記事では、Userクラスとその拡張クラス「Profileクラス」を使っての実例でした。
ポイントとして以下を覚えておきましょう。
- フォームクラスでは、ModelFormをベースとし、メソッドなどを引継ぎ記述すること
- adminクラスでは、対応している変数に正しい値を定義すること
ただしこのままだと、ユーザーのプロフィールを編集するページがありません。
ユーザーのプロフィールページを作るために、Djangoのsignalsを使って、ユーザーモデル保存後に自動でProfileクラスを生成する方法を解説します。