【Django入門12】管理画面のカスタマイズ(ModelAdmin)

このページにはプロモーションが含まれています

このページでは、Django での管理画面のカスタマイズについて解説していきます。

前回の連載となる下記ページでは管理画面の使い方について解説しました。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

今回は、この管理画面をカスタマイズする方法について解説していきます。

管理画面のカスタマイズの基本

まずは、この管理画面のカスタマイズについて、基本的な内容を解説していきたいと思います。

管理画面のカスタマイズが必要な理由

最初に、この管理画面のカスタマイズが必要である理由について解説しておきます。

前回の連載となる下記ページでも解説しましたが、管理画面ではモデルクラスのインスタンスの管理、より具体的にはインスタンスの追加・表示・編集・削除を行うことができます。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

ただし、デフォルトでは、管理画面でカスタマイズ可能なモデルクラスは UserGroup のみとなっています。これらは auth というアプリから提供されるモデルクラスとなります。これらは権限管理の機能を持つモデルクラスであり、この権限管理の機能によって、管理画面で実施可能な管理を “ユーザー” or “グループ” ごとに制限することができるようになっています。さらには User には認証に必要なフィールドや機能が用意されているため、この User を利用して管理画面へのログインも行うことが可能となっています。

ですが、前述の通り、デフォルトでは管理画面でカスタマイズ可能なモデルクラスは UserGroup のみとなっています。そのため、自身が開発するウェブアプリで定義したモデルクラスのインスタンスを管理画面で管理できるようにするためには、管理画面のカスタマイズを行う必要があります。

スポンサーリンク

管理画面のカスタマイズで行うこと

このカスタマイズとは、大きく分けると管理画面で管理したいモデルクラスの登録を行うことと、管理画面に表示するフォームや一覧表の詳細設定を行うことの2つに分けることができると思います。

モデルクラスの登録

管理画面で管理したいモデルクラスを登録すれば、そのモデルクラスのインスタンスが管理画面から管理できるようになります。

前述のとおり、この登録を行わなければ、ウェブアプリで定義したモデルクラスのインスタンスは管理画面で管理できないということになります。ウェブアプリを公開して運営していくのであれば、各種モデルクラスのインスタンスは管理画面から管理できるようにした方が良いので、このカスタマイズのやり方は必ず知っておいた方が良いです。

管理画面の詳細設定

モデルクラスの登録を行えば、そのモデルクラスのインスタンスの管理を実現することはできるのですが、追加フォームや編集フォームに表示されるフィールドの項目を変更したり、

管理画面の詳細設定例1

インスタンスの一覧表に表示される項目を変更したりするなど、管理画面の詳細設定を行うためには別途設定が必要になります。

管理画面の詳細設定例2

これらの設定を行うことで、よりインスタンスの管理を行いやすい管理画面を実現することができます。

こちらに関しては、もちろん使いやすさを向上させるという点で重要なのですが、モデルクラスの登録さえ行えば、この詳細設定を行わなくてもインスタンスの管理はできるので、余裕があれば設定方法を覚えておくくらいで良いと思います。

admin.py

これらの管理画面のカスタマイズについては admin.py というファイルで設定を行なっていくことになります。

この admin.pystartapp コマンド実行時に自動的に生成されるファイルになります。startapp コマンドはアプリを作成するときに実行するコマンドなので、admin.py はアプリ毎に用意されるファイルということになります。

アプリ内の models.py で定義したモデルクラスを管理画面で管理できるようにするためには、別途この admin.py の変更が必要となります。ただし、admin.py の変更は基本的には管理画面に対する設定を反映するためのものであり、ウェブアプリ本体に影響を与えるものではありません。したがって、管理画面での管理が不要である場合などは、admin.py の変更は不要となります。

管理画面のカスタマイズの仕方

次は管理画面のカスタマイズの仕方について解説していきます。

先ほどの説明の中で、管理画面のカスタマイズでは大きく分けて下記の2つを行うことになると説明しました。そして、この2つの両方とも admin.py の変更によって実現していくことになります。

  • モデルクラスの登録
  • 管理画面の詳細設定

そして、この2つの両方とも admin というモジュールから提供される関数やクラスを利用することで実現していくことになります。この admin は、下記のように初期状態の admin.py で既に import されているモジュール(Django フレームワークに用意されたアプリ)になります。

admin.pyの初期状態
from django.contrib import admin

# Register your models here.

スポンサーリンク

モデルクラスの登録の仕方

まず、モデルクラスの登録に関しては admin.site.register という関数を利用して行うことになります。この関数の第1引数に「管理画面で管理したいモデルクラス」を指定して実行することで、このモデルクラスが管理画面に管理対象として登録され、以降、このモデルクラスをのインスタンスが管理画面で管理可能となります。

モデルクラスの登録
from django.contrib import admin
from .models import モデルクラス

# Register your models here.
admin.site.register(モデルクラス)

例えば、models.pyStudent というモデルクラスを定義しているのであれば、下記のように admin.site.register を実行することで Student のインスタンスが管理画面で管理可能となります。

モデルクラスの登録例
from django.contrib import admin
from .models import Student

# Register your models here.
admin.site.register(Student)

逆に言うと、自身で定義したモデルクラスに関しては、admin.site.register でモデルクラスの登録を行わない限り、管理画面では管理することができません。なので、admin.py を自動生成された状態のまま使用していると、models.py でモデルクラスの定義を行ったとしても、それらのモデルクラスは管理画面では管理不可となります。

管理画面の詳細設定の仕方

また、フォームや一覧表の表示項目等の管理画面の詳細設定に関しても admin.site.register 関数を利用して行います。ただし、管理画面の詳細設定を行う場合、admin.site.register 関数には第1引数だけでなく、第2引数の指定が必要となります。そして、第2引数には「フォームや一覧表の詳細設定を行なったクラス」を指定します。

これにより、第1引数で指定したモデルクラスのインスタンスを管理する際、例えば一覧表を表示したり追加フォームを表示したりする際には、第2引数で指定したクラスに従った表示が行われるようになります。

そして、この第2引数に指定するクラスは admin.ModelAdmin というクラスのサブクラスとなります。以降、文章中では admin.ModelAdmin を単に ModelAdmin と呼びます。

つまり、フォームや一覧表の詳細設定を行うのであれば、下記のように admin.pyModelAdmin のサブクラス を定義し、その定義した ModelAdmin のサブクラスadmin.site.register の第2引数に指定する必要があります。

フォームや一覧表の詳細設定
from django.contrib import admin
from .models import モデルクラス

class ModelAdminのサブクラス(admin.ModelAdmin):
    # 略

admin.site.register(モデルクラス, ModelAdminのサブクラス)

ModelAdminModelAdmin のサブクラス

ModelAdmin では、一覧表に表示する項目やフォームに表示する項目等がクラス変数やメソッドによって定義されています。そして、admin.site.register の第2引数を指定しなかった場合、ModelAdmin の定義に基づいて一覧表やフォームが表示されることになります。

それに対し、admin.site.register の第2引数に ModelAdmin のサブクラス 指定した場合、第1引数に指定したモデルクラスのインスタンスの管理画面は、その第2引数に指定した ModelAdmin のサブクラス に従って表示されることになります。そのため、第2引数に指定する ModelAdmin のサブクラス で、スーパークラスである ModelAdmin のクラス変数を上書きしたり、メソッドを上書きしたり新規作成したりしておけば、通常とは異なる管理画面の表示を実現することができることになります。

例えば、ModelAdmin では、インスタンスの一覧表に表示するフィールド(列)が list_display というクラス変数で定義されています。具体的には、ModelAdmin では list_display = ('__str__', ) と定義されており、これは、インスタンスの一覧表のフィールドを __str__ のみ、すなわちインスタンスの出力結果(__str__ メソッドの実行結果)のみとするためのクラス変数の定義となります。これらのクラス変数はサブクラス側で上書き可能で、ModelAdmin のサブクラスlist_display を定義してやれば、list_display が上書きされて一覧表に表示されるフィールドを変化させることができます。

list_displayの上書き
class SubModelAdmin(admin.ModelAdmin):
    # 一覧表に__str__の実行結果とage・height・weightフィールドを表示する
    list_display = ('__str__', 'age', 'height', 'weight')

こんな感じで、ModelAdmin で定義されるクラス変数を ModelAdmin のサブクラス で再定義して上書きしてやることで、管理画面 に表示されるインスタンスの一覧表や、インスタンス追加・編集時のフォームをカスタマイズすることが可能です。

ただ、この上書きを行うためには、ModelAdmin で定義されるクラス変数やメソッド等を知っておく必要があります。次のカスタマイズの例の章でも少し例は示しますが、数が多くて本ページでは全てを紹介することができないため、ModelAdmin の定義を直接確認していただくのが良いかと思います。

この ModelAdmin は Django のインストールフォルダからの相対パスで下記の位置のファイルで定義されているため、定義を直接確認したいという方は是非参考にしてください。また、VSCode などの開発環境を利用していれば、ModelAdmin という文字列にカーソルを当てた状態での右クリックメニューから定義を調べるようなこともできます。

contrib/admin/options.py

UserAdmin

また、ModelAdmin のサブクラス の1つとして UserAdmin が Django フレームワークで定義されています。そして、User のインスタンスの一覧表や追加・編集フォームは、この UserAdmin に従って表示されるようになっています。

この UserAdmin は、django.contrib.auth.admin から提供されるクラスにであり、例えば UserAdmin では、先ほども例に挙げた list_display が下記のように定義されています。

UserAdminのlist_display
list_display = ("username", "email", "first_name", "last_name", "is_staff")

このように list_display が定義されているため、前回の連載の 管理画面の使い方 でも確認したように、User のインスタンスの一覧表は下の図のようなフィールドを持つことになります。

Userの一覧表から削除したいインスタンスが消える様子

前述のとおり、基本的には ModelAdmin のサブクラス を定義して一覧表やフォームの詳細設定を行なっていくことになるのですが、カスタムユーザー に関しては、ModelAdmin のサブクラス ではなく UserAdmin のサブクラス を定義して詳細設定を行うことをオススメします。カスタムユーザー は、User と同じく AbstractUser というクラスを継承して定義するため、User と同等のフィールドを持っています。なので、ModelAdmin のサブクラス を定義して一からユーザーを管理するモデルクラス向けの詳細設定を行うよりも、既に User を管理するモデルクラス向けの詳細設定が行われた UserAdmin のサブクラス を継承し、それをカスタマイズしていく方が楽です。

この UserAdmin のサブクラス を定義して管理画面で カスタムユーザー を管理するための手順や例は下記の専用のページで解説していますので、詳しく知りたい方は下記ページを参照していただければと思います。

管理画面でカスタムユーザーを管理する方法の説明ページアイキャッチ 【Django】管理画面でカスタムユーザーを管理する(UserAdmin)

また、UserAdmin の定義は ModelAdmin のサブクラス の例としても参考になると思いますので、管理画面のカスタマイズを行う際には UserAdmin の定義も確認してみるとよいと思います。

管理画面のカスタマイズ例

 では、先ほど説明したカスタマイズの仕方を踏まえ、次は実際に管理画面のカスタマイズ例を示していきたいと思います。

スポンサーリンク

カスタマイズを行うための準備

最初に、カスタマイズを行うための準備を行なっていきます。

前回の連載で管理画面の使うためには、最低限プロジェクトの作成、マイグレーション、スーパーユーザーの作成、開発用サーバーの起動が必要であると説明しました。さらに、今回はアプリ内で定義したモデルクラスを管理画面で管理できるようにする必要があるため、アプリの作成や models.py の変更を事前に行なっていきます。

プロジェクトの作成

まずは、適当な作業フォルダに移動してから下記コマンドで customadmin プロジェクトを生成します。

% django-admin startproject customadmin

これにより、customadmin というフォルダが作成されることになります。次は、cd コマンドで作成された customadmin フォルダに移動します。

% cd customadmin

アプリの作成

さらに、移動後のフォルダで下記コマンドを実行して management アプリを作成します。このアプリは生徒の管理を行うアプリを想定するものになります。

% python manage.py startapp management

続いて、今いるフォルダの中にある customadmin フォルダの settings.py を開き、下記のように INSTALLED_APPS の最初の要素に 'management', を追加して保存します。

settings.py
INSTALLED_APPS = [
    'management',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

これにより、先ほど作成したアプリ management がプロジェクトに登録され、開発用サーバー起動時等に managementmodels.pyadmin.py が読み込まれるようになります。逆に、プロジェクトにアプリの登録を行わなければ、models.pyadmin.py をどれだけ変更しても無視されることになるので注意してください。

モデルクラスの定義

次は、models.py を変更してモデルクラスの定義を行います。そして、今後の変更により、これらのモデルクラスを管理画面で管理できるようにカスタマイズを行なっていくことになります。

今回は下記の ClubStudentScore の3つのクラスを models.py で定義したいと思います。

models.py
from django.db import models

class Club(models.Model):
    name = models.CharField(max_length=256) # 名前

    def __str__(self):
        return self.name

class Student(models.Model):
    name = models.CharField(max_length=256) # 名前
    grade = models.IntegerField() # 学年
    is_staff = models.BooleanField() # 学級委員
    club = models.ForeignKey(Club, on_delete=models.CASCADE, null=True, blank=True) # 所属するクラブ

    def __str__(self):
        return self.name

class Score(models.Model):
    math = models.IntegerField() # 数学の点数
    science = models.IntegerField() # 理科の点数
    english = models.IntegerField() # 英語の点数
    average = models.FloatField() # 平均点
    student = models.OneToOneField(Student, on_delete=models.CASCADE) # 誰の点数か

    def __str__(self):
        return self.student.name + '\'s score'

Club はクラブ活動を管理するモデルクラス、Score はテストの点数を管理するモデルクラス、Student は生徒を管理するモデルクラスとなります。ClubStudent には ForeignKey でリレーションを設定しており、これにより各生徒が所属するクラブが管理できるようにしています。また、StudentScoreOneToOneField でリレーションを設定しており、各 Score がどの Student のものであるかを管理できるようにしています。

また、各モデルクラスで __str__ を定義している点は、管理画面のカスタマイズを行う上で結構重要なポイントとなります。後述でも解説しますが、管理画面からは __str__ メソッドが実行される機会が多く、管理画面をより使いやすくするためには __str__ メソッドを定義しておくほうが良いです。

マイグレーションの実施

モデルクラスの定義が済んだので、次はマイグレーションを実施して定義されているモデルクラスに対するテーブルの作成を行なっておきます。

まずは下記コマンドを実行し、先ほど定義したモデルクラスからマイグレーションの設定ファイルの作成を行います。これを忘れるとアプリ内で定義したモデルクラスに対するテーブルが作成されないので注意してください。

% python manage.py makemigrations

続いて、下記コマンドでマイグレーションを実行します。これにより、先ほど定義したモデルクラスだけでなく、settings.pyINSTALLED_APPS に指定されている各種アプリで定義されているモデルクラスに対応するテーブルがデータベースに作成されることになります。

% python manage.py migrate

今回は、models.py で定義したモデルクラスを管理画面で管理できるようにカスタマイズを行なっていくわけですから、当然 models.py で定義されたモデルクラスのテーブルが作成されることは重要となります。それに加え、管理画面へログインするためには authUser のテーブルを作成し、そのテーブルの中にスーパーユーザーのレコードを作成しておく必要があるため、上記の migrate によって authUser のテーブルが作成されることも重要となります。

スーパーユーザーの作成

次は、そのスーパーユーザーの作成を行います。

スーパーユーザーの作成は、下記コマンドを実行することで行うことができます。下記コマンドを実行するとユーザー名とメールアドレスとパスワード(2回)の入力が促されるため、それに従って情報の入力を行ってください。

% python manage.py createsuperuser

スーパーユーザーに関しては下記ページで解説していますので、スーパーユーザーについて詳しく知りたい方は下記ページを参照していただければと思います。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

現状の管理画面の確認

続いて、現状の管理画面の確認を行なっておきます。

まず、下記コマンドで Django の開発用サーバーを起動します。これにより、ウェブアプリや管理画面にウェブブラウザからアクセス可能な状態となります。

% python manage.py runserver

続いて、ウェブブラウザから下記 URL を開いてください。

http://localhost:8000/admin/

すると、管理画面のログインフォームが表示されますので、先ほど作成したスーパーユーザーのユーザー名とパスワードを入力して Log in ボタンをクリックしてください。

管理画面のログインフォーム

これにより、管理画面へのログインが行われ、下図のようなページが表示されると思います。そして、そこには管理対象として GroupsUsers のみが表示されていることが確認できるはずです。

管理画面で管理可能なモデルクラスがGroupとUserのみであることを示す図

この GroupUserauth アプリで定義されているモデルクラスであるため、先ほど自身で models.py に定義したモデルクラスに関しては1つも管理対象となっていないということになります。

このように、モデルクラスを定義しても、そのままでは管理画面で管理を行うことができません。管理画面で管理を行えるようにするためには、まずそのモデルクラスを管理画面での管理対象として登録する必要があります。

ということで、現状の確認は以上となります。次は、そのモデルクラスの登録を行なっていきますが、その前に一旦開発用サーバーは終了しておいてください。開発用サーバーが起動しているターミナルやコマンドプロンプト上で ctrl + c を入力すれば、開発用サーバーは終了するはずです。

モデルクラスの登録

ということで、次は models.py で定義したモデルクラスを管理画面での管理対象として登録していきたいと思います。

これに関しては、モデルクラスの登録の仕方 で解説したように、第1引数に登録したいモデルクラスを指定して admin.site.register 関数を実行することで実現することができます。

まずは management フォルダの下にある admin.py を開き、下記のように変更して保存してください。

モデルクラスの登録
from django.contrib import admin
from .models import Student, Score, Club

admin.site.register(Student)
admin.site.register(Score)
admin.site.register(Club)

このように、モデルクラスを管理画面の管理対象として登録するだけであれば admin.site.register の第1引数に登録したいモデルクラスを指定すれば良いだけなので、非常に簡単な手順で実現することができます。

スポンサーリンク

モデルクラス登録後の管理画面の確認

admin.py の変更によってモデルクラスの登録が行われるようになったため、この登録後の管理画面の確認を行なっていきたいと思います。

まず、再度下記コマンドを実行して開発用サーバーを起動し、

% python manage.py runserver

再度ウェブブラウザから下記 URL を開いてください。

http://localhost:8000/admin/

先ほどログアウトを行なっていない場合、管理画面のトップページが直接開かれると思います。ログアウトされている場合は、再度スーパーユーザーのユーザー名とパスワードを入力してログインして管理画面のトップページを開いてください。

まず、管理画面のトップページで注目していただきたいのが、管理対象のモデルとして management アプリの models.py で定義した ClubScoreStudent の3つが表示されるようになっている点になります(リンクとしては、それぞれの名前に s が付加された複数形で表示されることになります。)。

管理対象のモデルクラスが増えている様子

そして、これらの登録されたモデルクラスのインスタンスに関しては、下記ページで説明した手順と同様の手順で追加・表示・編集・削除が行えるようになっています(表示されるフォームや一覧表は異なります)。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

例えば、Clubs の右側にある Add リンクをクリックすれば、Club の追加フォームが表示され、Club のインスタンスの追加を行うことができます。

Clubの追加フォーム

また、Club のインスタンスの追加後に Students の右側にある Add リンクをクリックすれば Student の追加フォームが表示され、Student のインスタンスの追加を行うことができます。また、この追加フォームでは Club フィールドから関連付け相手となる Club のインスタンスが選択可能であることも確認できます。

Studentの追加フォーム

このように、モデルクラスの登録を行なっておけば、それらのモデルクラスのインスタンスの管理を管理画面から行うことができるようになります。まだ登録しただけでフォーム等の詳細設定は行っていませんが、それなりにしっかりした管理画面が構成されており、これでも十分インスタンスの管理を行うことができることを確認できると思います。

ただ、完璧な管理画面かといえばそうでもないです。

例えば、下記は Student のインスタンスの一覧表であり、この一覧画面には生徒の名前(name)しか表示されないようになっています。Studentname 以外にも gradeclub 等のフィールドも持っていますので、これらの情報も一覧表に表示されるとより便利になります。

変更前のStudentのインスタンスの一覧表

また、下図は Score の追加フォームとなります。Score の持つ全フィールドの情報が入力可能なフォームとなっていることが確認できると思います。もちろん、この追加フォームからも Score のインスタンスを追加することは可能ではありますが、このフォームの場合、平均点を示す Average をわざわざユーザーが計算して入力する必要があり不便です。他の科目の点数から自動的に算出するようにしてあげたほうが便利ですね。

変更前のScoreの追加フォーム

このように、モデルクラスを登録することで、そのモデルクラスのインスタンスの管理を行うことは可能です。ですが、そのままの画面だと不便な場合がありますので、そういった場合は詳細設定を行い、より便利な管理画面を実現していくことをオススメします。

ModelAdmin のサブクラス の定義

では、そういった詳細設定を行う例を示していきたいと思います。

管理画面の詳細設定の仕方 でも説明しましたが、各モデルクラスに対する管理画面(フォームやインスタンスの一覧表)は、デフォルトでは ModelAdmin というクラスに従って表示されるようになっています。つまり、これらが表示される際には ModelAdmin が参照されることになります。そして、この時の参照先のクラスは ModelAdmin のサブクラス に設定することが可能です。また、この参照先のクラスはモデルクラスごとに個別に設定可能です。

したがって、下記の実装を行えば、新たに定義したクラスに従って各モデルクラスに対するフォームやインスタンスの一覧表の表示が行われるようになります。これらは全て、admin.py で実装していくことになります。

  • ModelAdmin のサブクラス として新たなクラスを定義
  • その ModelAdmin のサブクラス でクラス変数やメソッドを定義して ModelAdmin の定義を上書き
  • その ModelAdmin のサブクラス を管理画面表示時の参照先のクラスとして設定

以降では、まずは ModelAdmin のサブクラスの定義のみを行い、さらに、そのサブクラスを管理画面表示時の参照先のクラスに設定していきます。そして、その後に、ModelAdmin のサブクラス にクラス変数やメソッドを追加する例を示していきます。

ModelAdmin のサブクラスの定義

今回は、StudentScore のフォームやインスタンス一覧表の変更を行うため、まずは下記のように admin.py を変更し、StudentAdminScoreAdmin を定義します。 

ModelAdminのサブクラスの定義
from django.contrib import admin
from .models import Student, Score, Club

class StudentAdmin(admin.ModelAdmin):
    pass

class ScoreAdmin(admin.ModelAdmin):
    pass

admin.site.register(Student)
admin.site.register(Score)
admin.site.register(Club)

ひとまず、StudentAdminScoreAdmin とは ModelAdmin を継承するのみとなっています。これらは後述の説明の中で変更を行なっていきます。

各モデルクラスに対する ModelAdmin の設定

また、上記のように admin.py で StudentAdminScoreAdmin を定義したものの、これだけではこれらのクラスは管理画面表示時には参照されません。管理画面表示時に参照させるためには、これらのクラスを第2引数に指定して admin.site.register を実行する必要があります。

今回は、Student に対する管理画面(フォームやインタンスの一覧表の表示画面)を StudentAdmin に従って表示されるようにし、さらに Score に対する管理画面(フォームやインタンスの一覧表の表示画面)を ScoretAdmin に従って表示されるようにしたいため、下記のように admin.py を変更します。

ModelAdminのサブクラスの定義
from django.contrib import admin
from .models import Student, Score, Club

class StudentAdmin(admin.ModelAdmin):
    pass

class ScoreAdmin(admin.ModelAdmin):
    pass

admin.site.register(Student, StudentAdmin)
admin.site.register(Score, ScoreAdmin)
admin.site.register(Club)

このように、admin.site.register の第1引数にモデルクラスを指定することで、そのモデルクラスが管理画面での管理対象として追加されることになります。さらに、admin.site.register の第2引数に ModelAdmin のサブクラスを指定することで、第1引数に指定したモデルクラスを管理する画面が第2引数に指定したクラスに基づいて表示されるようになります。

register関数の引数の意味を示す図

フォームの変更

ということで、先ほどの admin.py の変更によって、Student に対する管理画面と Score に対する管理画面が、それぞれ StudentAdminScoreAdmin に従って表示されるようになります。なので、StudentAdmin や ScoreAdmin を変更してやれば、それに従って表示される管理画面が変化することになります。

その変更の実例を、ここからいくつか紹介していきたいと思います。

まず、ScoreAdmin を変更し、今まで下図のように追加フォームや編集フォームで表示されていた average を削除するようにしたいと思います。

Scoreの追加フォーム

そして、その代わりに、フォームから SAVE ボタンがクリックされてフォームの各種フィールドの値がレコードとしてデータベースに保存される際に、average のフィールドの値を自動的に算出して average の値も保存されるようにしたいと思います。

これにより、average のフィールドの値入力時にユーザーは平均値を計算する手間がなくなり、Score のインスタンスの追加が楽になります。

フォームからのフィールドの削除

まず、average フィールドが表示されないように、フォームから average フィールドの削除を行います。

ModelAdmin では、追加フォームや編集フォームでモデルクラスの持つ全フィールドの入力を受け付けるようになっています。Score モデルクラスは models.py で下記のように定義しているため、上の図で示すように、追加フォームや編集フォームでは mathscienceenglishaveragestudent の全てのフィールドの入力を受け付けるようになっています。

Score
class Score(models.Model):
    math = models.IntegerField() # 数学の点数
    science = models.IntegerField() # 理科の点数
    english = models.IntegerField() # 英語の点数
    average = models.FloatField() # 平均点
    student = models.OneToOneField(Student, on_delete=models.CASCADE) # 誰の点数か

    def __str__(self):
        return self.student.name + '\'s score'

全てのフィールドの入力受付を行うのではなく、入力受付を行うフィールドを限定したい場合、ModelAdmin のサブクラス側で fields もしくは exclude というクラス変数のいずれかを定義する必要があります。

fields では、モデルクラスの持つフィールドのうち、入力受付を行いたいフィールドをリスト or タプル形式で指定します。exclude では入力受付を行いたくなフィールドをリスト or タプル形式で指定します。今回は average の入力受付を行いたくないのですから、下記のいずれかを指定すれば良いことになります。

  • fields = ('math', 'science', 'english', 'student')
  • exclude = ('average',)

ということで、今回は exclude を利用して下記のように ScoreAdmin を変更したいと思います。これにより、フォームから average フィールドが消えます。

averageフィールドの削除
class ScoreAdmin(admin.ModelAdmin):
    exclude = ('average',)

フィールドが消える理由をもう少し詳しく説明しておくと、excludeModelAdmin の持つクラス変数であり、exclude = None として定義されています。なので、基本的にはモデルクラスの持つフィールドが全てフォームに表示されるようになっています。

ですが、スーパークラス側、つまり ModelAdmin 側の持つクラス変数をサブクラス側でも定義すれば、サブクラス側の定義によってクラス変数が上書きされ、それを利用して処理が実行されることになります。今回の場合は、その上書きされたクラス変数に従って管理画面が表示されることになります。ですので、ModelAdmin のサブクラス側で exclude を定義してやれば、その exclude の定義に従って管理画面が表示されることになり、このクラス変数の場合は指定されたフィールドがフォームから削除されることになります。

この excludeModelAdmin の持つクラス変数の一例であり、他のクラス変数も同様に上書きを行うことで管理画面の表示を変更することが可能となります。その例は インスタンスの一覧表の変更 でも紹介します。

保存時の処理の上書き

上記の ScoreAdmin の変更によって、フォームから average フィールドが消えることになります。これにより、ユーザーは average フィールドの値の入力が不要となります。ですが、当然 Score モデルクラスには average フィールドが存在しています。

したがって、例えば Score の追加フォームで Score のインスタンスの追加を行おうとしても、現状のフォームには average フィールドが存在しないため、インスタンスの average フィールドには値がセットされないままデータベースへの保存が行われることになります。average フィールドは必須フィールドとなっていますので、値がセットされないままデータベースに保存しようとすると例外が発生することになります。つまり、現状ではフォームで SAVE ボタンがクリックされた際には必ず例外が発生します。

これを防ぐためには、SAVE ボタンがクリックされた際に Score のインスタンスの average フィールドに各教科の平均値をセットするように処理を変更してやる必要があります。つまり、SAVE ボタンがクリックされた際に実行されるメソッドを変更する必要があります。

SAVE ボタンがクリックされた際に実行されるメソッドはいくつかあるのですが、インスタンスの保存が行われるのは ModelAdmin クラスの save_model というメソッドになります。

Modelminのsave_model
def save_model(self, request, obj, form, change):
    """
    Given a model instance save it to the database.
    """
    obj.save()

したがって、ModelAdmin のサブクラス である ScoreAdminsave_model というメソッドを追加してやれば、save_model メソッドのオーバーライド、つまり上書きが行われ、save_model が実行された際には ModelAdmin のものではなく ScoreAdmin で定義された save_model が実行されることになります。

save_modelメソッドを上書きする様子

なので、ScoreAdmin に save_model メソッドを定義し、その中で average フィールドの値をセットするようにしてやれば、上記の例外が発生する問題は解決することができます。

具体的には、下記のような save_model メソッドを ScoreAdmin クラスに定義してやれば良いです。

save_modelのオーバーライド
class ScoreAdmin(admin.ModelAdmin):
    exclude = ('average',)

    def save_model(self, request, obj, form, change):
        obj.average = (obj.math + obj.science + obj.english) / 3 
        super().save_model(request, obj, form, change)

ポイントは、引数をスーパークラス、すなわち ModelAdminsave_model メソッドに合わせる必要がある点になります。また、保存するインスタンスは引数 obj として渡されてくることになりますので、objaverage に平均値の算出結果をセットする必要があります。

このように、メソッドのオーバーライドの仕組みを利用すれば、フォームからの操作時の処理を上書きして変更するようなことも可能です。

補足しておくと、今回は ScoreAdmin でのオーバーライドを利用しましたが、実は Score モデルクラスに save メソッドを追加してオーバーライドを行い、そこで average に値をセットするのでも良いです。むしろそちらの方が適切だと思います。が、今回は管理画面のカスタマイズを行う上でオーバーライドも利用できるという点を説明したかったため、あえて ScoreAdmin でのオーバーライドを利用した例を示しています。

スポンサーリンク

インスタンスの一覧表の変更

続いて、インスタンスの一覧表の変更を行なっていきたいと思います。例えば、現状の Student のインスタンスの一覧表は下の図のようになっており、生徒の名前のみが表示されるようになっています。

現状のStudentのインスタンスの一覧表

Student には、名前だけでなく学年を示す grade フィールドが存在しますし、学級委員かどうかを示す is_staff フィールド、所属するクラブを示す club フィールドも存在しています。また、StudentScore との間にリレーションが設定されているため、生徒のテストの点数も管理されていることになります。これらの情報も表示されると、一覧表から読み取れる情報が多くなって、より便利になります。ということで、まずは一覧表に表示されるフィールドを追加していきます。

また、前回の連載で User のインスタンス一覧を確認しましたが、User の一覧表のページには、下の図の上側部分のようにサーチバーが存在していたり、右側部分のようにフィルター機能が存在したりしています。これについても Student の一覧表のページに追加を行なっていきたいと思います。

Userのインスタンスの一覧表

一覧表の表示フィールドの追加(フィールドの表示)

まずは、一覧表の表示フィールド(表示項目・表示列)の追加を行なっていきたいと思います。

フォームの変更 ではフォームに表示するフィールドの削除を行うために、ModelAdmin のサブクラスexclude というクラス変数の定義を行いました。

同様に、ModelAdmin のサブクラス でクラス変数を定義することで、一覧表の表示フィールドの変更を行うことが可能となります。この場合は、list_display というクラス変数を定義することになります。list_display に表示したいフィールド名を要素とするリスト or タプルを指定してやれば、そのフィールドが一覧表に表示されるようになります。

ここで、ModelAdmin での list_display の定義を確認してみましょう!ModelAdmin は list_display が下記のように定義されています。

ModelAdminでのlist_displayの定義
list_display = ('__str__',)

この '__str__' の意味合いについては次の 一覧表の表示フィールドの追加(メソッドの実行) で解説しますが、これはモデルクラスの__str__ メソッドの実行結果を表示することを指示する指定になります。上記のように list_display に指定されているタプルには '__str__'  の要素しか存在しませんので、現状の一覧表には、各インスタンスに対して __str__ メソッドの実行結果のみが表示されるようになっていることになります。

一覧表に表示されているフィールドがlist_displayの指定に従っていることを示す図

このように、一覧表で表示されるフィールドは list_display の指定によって決まります。そのため、ModelAdmin のサブクラス 側で list_display を定義してやれば、list_display の定義が上書きされ、その list_display への指定に基づいて一覧表が表示されるようになります。

今回は、まずは Student の持つ全フィールドを表示するように list_display を定義したいと思います。Student は モデルクラスの定義 で定義しており、namegradeis_staffclub のフィールドを持っています。そのため、これらのフィールドを要素とするタプルを list_display に指定するように、下記のように StudentAdmin の定義を変更したいと思います。

一覧表への表示フィールドの追加
class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'grade', 'is_staff', 'club')

このように定義を変更すれば、Student のインスタンスの一覧表が下の図のように変化します。list_display に指定したリストに従って表示されるフィールドが変化していることが確認できると思います。

list_displayにフィールド追加後の一覧表

ただ、上の図を見てみると、以前に示したものに比べて1列目のフィールド名(列名)が STUDENT から NAME に変わっていることが確認できると思います。

これは、list_display の最初の要素を '__str__' から name に変更したためです。フィールド名(列名)としては、基本的に list_display に指定したものが表示されることになるのですが、'__str__' を指定した場合はモデルクラス名がそのまま出力されることになります。そして、各フィールド(セル)には __str__ メソッドの実行結果が表示されることになります。

Student__str__ メソッドはインスタンスの name フィールドを返却するようになっていますので、list_display'__str__' を指定した場合と name を指定した場合とでは、各フィールド(セル)の表示結果は同じことになります。が、フィールド名(列名)は異なることになるので注意してください。

また、'__str__' を指定した場合は、モデルクラスに __str__ が用意されていないと、下図のようなオブジェクト名がそのまま表示されることになるので注意してください。

__str__メソッドが定義されていない場合の表示結果

さらに、club フィールドに関しては Club との関連付けを行うための ForeignKey となっているのですが、このような場合でも、それぞれの Student のインスタンスとの関連付け相手となる Club の情報が表示されるようになっています。そして、この場合も、Club の情報としては Club__str__ メソッドの実行結果が表示されることになります。前述の通り、Club__str__  メソッドを定義していない場合はオブジェクト名がそのまま表示されることになるので注意してください。

ここでの説明でも分かるように、管理画面ではモデルクラスの __str__  メソッドが利用されることが多いです。管理画面を使いやすくするためにも __str__ メソッドをモデルクラスに定義しておくことをオススメします。

一覧表の表示フィールドの追加(メソッドの実行)

さて、先ほど Student のインスタンス一覧表に追加したフィールドは全て、Student の持つフィールドとなります。モデルクラスの持つフィールドを list_display に指定した場合、各インスタンスのフィールドにセットされている値がそのまま一覧表に表示されることになります(is_staff のように CSS などによって装飾されたりするものもありますが)。

各種フィールドにセットされている値が出力される様子

実は、この list_display には、ModelAdmin のサブクラス or そのモデルクラスに定義されたメソッド名を指定することも可能です。この場合、一覧表には、メソッドの返却値が表示されることになります。

例えば、下記のように StudentAdmin を定義したとします。

存在しないフィールドの表示
class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'grade', 'is_staff', 'club', 'rank')

    def rank(self, obj):
        if not hasattr(obj, 'score'):
            return 'N/A'
        
        scores = Score.objects.order_by('average').reverse()
        i = 0
        for score in scores:
            if score.student.grade == obj.grade:
                i += 1
                if score.student.id == obj.id:
                    break

        return i

この場合、list_display には Student のフィールドには存在しない 'rank' が指定されています。そのため、Student のインスタンスの一覧表示時には StudentAdminrank メソッドが実行され、その実行結果(return した値)が一覧表の rank フィールドに表示されることになります。

この実行されるメソッドの引数は selfobj となり、self が StudentAdmin のインスタンス、obj が Student のインスタンスとなります。特に、Student のインスタンスが引数で渡されて利用可能となるため、Student のインスタンスの持つデータ属性等を利用して様々な処理を実行することが可能となります。上記の rank では、obj と同じ学年(grade)の中でのテストの点数の平均点(average)の順位を返却するようにしています。メソッドの処理の書き方がイマイチな気もしますが、obj の情報や他のモデルクラスを利用した様々な処理が実現可能であることは理解していただけるのでは無いかと思います。

実際の rank フィールドを追加した Student のインスタンスの一覧表の表示結果は下の図のようになります。前述の通り、RANK の列の各フィールドには rank メソッドの return した値が表示されています。

rankフィールドを追加した一覧表

このように、list_display にメソッド名を指定することで、そのメソッドの返却値を一覧表に表示することが可能となります。このメソッドは、ModelAdmin のサブクラス側に用意しても良いですし、モデルクラス側に用意しても良いです(モデルクラス側に用意する場合は、引数 obj が不要となります)。この両方にフィールド名と名前が一致するメソッドが存在する場合、ModelAdmin のサブクラス 側のものが優先して実行されることになります。逆に、その両方に list_display に指定したメソッド名のメソッドが存在しない場合は例外が発生することになります。

基本的には、ビュー等からも利用される可能性のあるメソッドであればモデルクラス側に用意してやれば良いと思います。例えば、上記の rank メソッドは StudentAdmin に用意しているため、基本的には管理画面からのみ利用されるメソッドとなります。ビュー等から利用したい場合は、Studen 側にメソッドを移動してあげた方が良いです。

モデルクラスへのメソッドの移動
class Student(models.Model):
    # 略
    
    def rank(self):
        obj = self
        if not hasattr(obj, 'score'):
            return 'N/A'
        
        scores = Score.objects.order_by('average').reverse()
        i = 0
        for score in scores:
            if score.student.grade == obj.grade:
                i += 1
                if score.student.id == obj.id:
                    break

        return i

フィルターの追加

続いてフィルター機能の追加を行なっていきます。フィルター機能を利用することにより、一覧表に表示されるインスタンスを「指定した条件に当てはまるもののみ」に絞り込むことができます。例えば Student の例であれば、一覧表に is_staffTrue のインスタンスのみを表示したり、grade2 のインスタンスのみを表示したりするようなことができます。

このフィルター機能についても、今まで通り ModelAdmin のサブクラス にクラス変数を定義することで実現することができます。フィルター機能を実現するために定義が必要なクラス変数は list_filter となります。この list_filter に関してもリスト形式 or タプル形式のデータを指定する必要があり、各要素には、一覧表に表示するインスタンスの「表示条件を指定するフィールド」の名前を指定します。

例えば下記のように StudentAdmin を変更すれば、is_staff フィールドと grade フィールドの2つが表示条件として追加されることになります。

list_filterの定義
class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'grade', 'is_staff', 'club', 'rank')
    list_filter = ('is_staff', 'grade')

    def rank(self, obj):
        # 略

このように、list_filter の定義を行なった場合、下の図で示すように一覧表の右側に FILTER というセクションが表示されるようになります。

list_filterを定義した場合の一覧表

この FILTER というセクションで、例えば By is staff の下側に表示されている Yes をクリックすれば、一覧表に表示される Student のインスタンスは is_staff フィールドが True のもののみに絞り込まれることになります。

フィルター機能の利用例

同様に、By grade の下側に表示さている 2 をクリックすれば、一覧表に表示される Student のインスタンスは grade フィールドが 2 のもののみに絞り込まれることになります。

このように、ModelAdmin のサブクラスで list_filter を定義して表示条件となるフィールド名を指定しておけば、一覧表の右側に表示する条件となる選択肢がリンクとして表示されるようになります。そして、そのリンクをクリックすることで一覧表に表示されるインスタンスを絞り込むことができるようになります。

サーチバーの追加

最後にサーチバーの追加を行なっていきます。このサーチバーを追加することで、一覧表から特定のインスタンスを検索できるようになります。

例の如く実現方法は今までと同様で、ModelAdmin のサブクラスsearch_fields を定義してやれば良いだけです。これも今までと同様で、search_fields には検索対象とするフィールド名を要素とするリスト or タプルを指定します。

例えば下記のように StudentAdmin を変更すれば、一覧表の上側にサーチバーが追加され、そのサーチバーで name フィールドに対する検索を行うことができるようになります。

search_fieldsの定義
class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'grade', 'is_staff', 'club', 'rank')
    list_filter = ('is_staff', 'grade')
    search_fields = ('name',)

    def rank(self, obj):
        # 略

実際に Student のインスタンスの一覧表を表示した結果が下の図となり、一覧表の上側にサーチバーが追加されていることが確認できます。

サーチバーが追加された様子

さらに、サーチバーに yamada と入力して Search ボタンをクリックすれば、name フィールドに yamada という文字列が含まれるインスタンスのみが一覧表に表示されることになります。

サーチバーの利用例

カスタマイズ例のまとめ

ここまで説明してきた内容を反映した admin.py は下記のようになります。基本的に、今まで紹介したソースコードを統合したものになりますが、ScoreAdminlist_display の定義を追加したりもしています。いずれにしても、ここまで解説を読んでくださった方であれば、各クラス変数やメソッドの意味合いは理解していただけると思います。

admin.py
from django.contrib import admin
from .models import Student, Score, Club

class StudentAdmin(admin.ModelAdmin):
    list_display = ('name', 'grade', 'is_staff', 'club', 'rank')
    list_filter = ('is_staff', 'grade')
    search_fields = ('name',)

    def rank(self, obj):
        if not hasattr(obj, 'score'):
            return 'N/A'
        
        scores = Score.objects.order_by('average').reverse()
        i = 0
        for score in scores:
            if score.student.grade == obj.grade:
                i += 1
                if score.student.id == obj.id:
                    break

        return i

class ScoreAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'math', 'science', 'english', 'average')
    exclude = ('average',)

    def save_model(self, request, obj, form, change):
        obj.average = (obj.math + obj.science + obj.english) / 3 
        super().save_model(request, obj, form, change)


class ClubAdmin(admin.ModelAdmin):
    pass
    
admin.site.register(Student, StudentAdmin)
admin.site.register(Score, ScoreAdmin)
admin.site.register(Club, ClubAdmin)

admin.py を上記のように変更した後に開発用サーバーを起動して管理画面にログインすれば、最初にログインした時とは各モデルクラスのインスタンスの一覧表の見た目や、特に Score に対するフォームで average の入力が不要になったことが確認できると思います。そして、このように管理画面が変化したのは、ModelAdmin のサブクラス の定義を行い、それを admin.site.register によって管理画面表示時に参照されるクラスとして登録したからになります。

このように、ModelAdmin のサブクラス の定義によって各モデルクラスに対する管理画面を自分好みのものに変更することが可能です。いくつか例を示してきましたが、これらは変更例のほんの一部であり、ModelAdmin のサブクラス の変更によって様々な管理画面を実現することができます。

基本的には、この管理画面の変更は ModelAdmin のサブクラス 側で ModelAdmin のクラス変数・メソッドを上書きすることによって実現することができますので、もっと色んな変更をしてみたいという方は、是非 ModelAdmin に存在するクラス変数やメソッドを調べてみていただければと思います。

また、このスーパークラス側のクラス変数やメソッドの上書きに関しては、Django では管理画面のカスタマイズだけでなく様々な場面で使える考え方になりますので、是非この点については覚えておいてください。

掲示板アプリで管理画面をカスタマイズしてみる

最後に、いつも通りの流れで、この Django 入門 の連載の中で開発してきている掲示板アプリに対し、管理画面のカスタマイズを行なっていきたいと思います。

この Django 入門 に関しては連載形式となっており、ここでは以前に下記ページの 掲示板アプリにログイン機能を導入してみる で作成したウェブアプリに対して管理画面のカスタマイズを行う例を示していきたいと思います。

ログインの実現方法の解説ページアイキャッチ 【Django入門10】ログイン機能の実現

といっても、ここまでの解説でカスタマイズの例は十分示せたと思っていますので、今回はサラッと簡単にカスタマイズを行なっていきたいと思います。

スポンサーリンク

掲示板アプリのプロジェクト一式の公開先

この Django 入門 の連載を通して開発している掲示板アプリのプロジェクトは GitHub の下記レポジトリで公開しています。

https://github.com/da-eu/django-introduction

また、前述のとおり、ここでは前回の連載の 掲示板アプリにログイン機能を導入してみる で作成したプロジェクトをベースに変更を加えていきます。このベースとなるプロジェクトは下記のリリースで公開していますので、必要に応じてこちらからプロジェクト一式を取得してください。

https://github.com/da-eu/django-introduction/releases/tag/django-login

さらに、ここから説明していく内容の変更を加えたプロジェクトも下記のリリースで公開しています。以降では、基本的には前回からの差分のみのコードを紹介していくことになるため、変更後のソースコードの全体を見たいという方は、下記からプロジェクト一式を取得してください。

https://github.com/da-eu/django-introduction/releases/tag/django-modeladmin

admin.py の変更

今まで管理画面のカスタマイズを customadmin プロジェクトで実施してきましたが、ここからは掲示板アプリで管理画面のカスタマイズを行なっていくことになるため、customadmin プロジェクトで起動した開発用サーバーを終了し(runserver コマンド実行中のターミナル等で ctrl + c を入力)、掲示板アプリを開発しているプロジェクトである testproject のフォルダに移動してください。

さらに、その移動後に、forum フォルダの下にある admin.py を開き、下記のように変更を行なってください。

admin.py
from django.contrib import admin
from .models import Comment
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _

User = get_user_model()

class CommentAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'user')

class CustomUserAdmin(UserAdmin):
    list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'age')

    fieldsets = (
        (None, {'fields': ('username', 'age', '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', 'age', 'password1', 'password2'),
        }),
    )

admin.site.register(User, CustomUserAdmin)
admin.site.register(Comment, CommentAdmin)

CommentAdmin に関しては、list_display を定義しているだけで、定義内容も簡単なので省略します。

それに対し、CustomUserAdmin の定義は非常に複雑に思えます。が、実はこれも基本的な考え方は今までの説明と同様になります。要は、スーパークラスのクラス変数をサブクラス側で上書きしているだけです。

CustomUserAdmin は、UserAdmin の節で紹介した UserAdmin のサブクラス であり、UserAdmin では list_displayfieldsetsadd_fieldsets の定義は下記のようになっています。

UserAdminでのクラス変数の定義
class UserAdmin(admin.ModelAdmin):

    list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
    
    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'),
        }),
    )

つまり、admin.py に新たに定義した CustomUserAdmin では、UserAdminlist_displayfieldsetsadd_fieldsets それぞれに対して age フィールドを追加しているだけです。

この変更に関しても、今まで説明してきた ModelAdmin のサブクラス 側でスーパークラスのクラス変数を上書きするという考え方に変わりはありません。UserAdminModelAdmin のサブクラス の1つですので、UserAdmin のサブクラスModelAdmin のサブクラス と考えられます。

そして、この UserAdminauth というアプリで定義されたクラスであり、User の管理専用にカスタマイズされたクラスとなります。それを継承する形で CustomUserAdmin を定義することで、User ではなく CustomUser (get_user_model によって返却されるモデルクラス) の管理専用のクラスにカスタマイズしています。具体的には、models.py に定義している CustomUser では User に比べて age フィールドを追加で定義しているため、この age フィールドも扱えるように list_displayfieldsetsadd_fieldsets'age' を追加しているというわけです。

UserAdmin では ModelAdmin に比べて追加フォームの表示項目を設定するクラス変数として add_fieldsets が追加されているという違いはあるものの、基本的には継承するサブクラスのクラス変数を上書きするという考え方に基づいて実装すれば良いだけになります。

また、この UserAdmin に関しては下記ページで解説していますので、詳しくは下記ページを参照していただければと思います。

管理画面でカスタムユーザーを管理する方法の説明ページアイキャッチ 【Django】管理画面でカスタムユーザーを管理する(UserAdmin)

以上により、管理画面のカスタマイズに関しては完了となります。

動作確認

ということで、これで管理画面のカスタマイズ結果を確認可能な準備が整ったことになります。

次は、実際にカスタマイズ後の管理画面を利用して動作確認を行なっていきましょう!基本的な管理画面の使い方は下記ページで解説していますので、まだ使ったことがない方は下記ページも参考にしながら使ってみてください。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

スーパーユーザーの作成

まず、管理画面へログインするためにスーパーユーザーを作成しましょう!前回の連載の下記ページの動作確認でもスーパーユーザーを作成しましたが、これは掲示板アプリのスーパーユーザではないため、別途掲示板アプリ用のスーパーユーザーを作成する必要があります。

Djangoの管理画面の使い方の解説ページアイキャッチ 【Django入門11】管理画面(admin)の使い方の基本

ただ、手順は同様で、testproject フォルダの中に移動し、そのフォルダにある manage.py を利用して下記のように createsuperuser コマンドを実行してやれば良いです。既に存在しているユーザー名を指定するとエラーとなるので注意してください。

% python manage.py createsuperuser                           
ユーザー名: YamadaAdm
メールアドレス: adm@example.com
Age: 18
Password: 
Password (again): 
Superuser created successfully.

開発用サーバーの起動

次は、下記コマンドを実行して開発用サーバーを起動してください。他のプロジェクトの開発用サーバーを起動していると、ポートが既に使用されていて起動に失敗することになりますので注意してください。

% python manage.py runserver

管理画面へのログイン

続いてウェブブラウザを起動し、下記 URL を開いてください。

http://localhost:8000/admin/

これにより、下図のような管理画面のログインフォームが表示されるはずです。ユーザー名とパスワードには、先ほどスーパーユーザー作成時に指定したものを入力してください。そして、入力後に ログイン ボタンをクリックしてください。

管理画面へのログインフォーム

ログインに成功すれば、下の図のような管理画面のトップページに遷移するはずです。

掲示板アプリにおける管理画面のトップページ

この画面は、管理画面のカスタマイズ例 で確認した管理画面と比較して大きく異なる点が2つあります。

1つ目の違いは、表示される言語が異なる点になります。上の図のように testproject の管理画面は日本語化されています。この日本語化が行われている理由は settings.py で LANGUAGE_CODE を下記のように指定しているからになります。

LANGUAGE_CODE
LANGUAGE_CODE = 'ja'

このように、LANGUAGE_CODE の指定によって管理画面の大部分は指定された言語に翻訳されて表示されることになります。独自で定義したモデルクラスの名前などは一部翻訳されていませんが、これらも日本語で表示させようと思えば可能です。

2つ目の違いは、日本語化されているので少し分かりにくいですが、ユーザー  モデルクラス (Users) が 管理画面のカスタマイズ例 では AUTHENTICATION AND AUTHORIZATION に存在していたのに対し、上図においては 認証と認可 から消え、FORUM に移動している点となります。認証と認可AUTHENTICATION AND AUTHORIZATION を日本語化した結果であり、両方とも auth アプリであることを示しています。

authアプリにUsersが存在する例

forum アプリの admin.pyadmin.site.register(CustomUser, CustomUserAdmin) を実行しているため、FORUMユーザー が存在していることに関しては、まぁ納得できるのではないかと思います。

では、auth から ユーザー が消えている理由は何なのでしょうか?

結論としては、これも settings.py による設定が主な理由となります。掲示板アプリに CustomUser を定義した際に、下記のように settings.py へ AUTH_USER_MODEL の設定を追記し、AUTH_USER_MODEL のデフォルト値  'auth.User' から 'forum.CustomUser' への変更を行なっています。

AUTH_USER_MODEL
AUTH_USER_MODEL = 'forum.CustomUser'

このように、AUTH_USER_MODEL の変更を行なった場合、auth アプリ側では ユーザー の管理画面への登録が行われないようです。なので、auth アプリには ユーザー モデルクラスは表示されません。

逆に、AUTH_USER_MODEL を変更しなかった場合は、auth アプリ側で ユーザー の管理画面への登録が行われることになり、auth アプリには ユーザー が存在することになります。管理画面のカスタマイズ例 では AUTH_USER_MODEL の変更を行なっていないため、auth アプリには ユーザー (Users) が存在していたということになります。

CustomUserAdmin の確認

話が少し逸れましたが、次は管理画面のトップページにおける ユーザー の右側にある 追加 リンクをクリックしてみてください。

クリックすれば、下の図のような画面に遷移し、ここで表示されるフォームから CustomUser のインスタンスの追加を行うことができます。

CustomUserの追加フォーム

このインスタンスの追加フォームに age フィールドが存在するのは CustomUserAdminadd_fieldsets を追加し、そこで 'age' を指定しているからななります。

また、インスタンスを追加して一覧表を表示すれば下図のように AGE フィールド表示されていることが確認できると思います。これも CustomUserAdminlist_display の定義によるものになります。

CustomUserのインスタンス一覧表

同様に編集フォームでも age フィールドの変更が可能であることが確認できると思います。

このように、CustomUser を管理するページやフォームは CustomUserAdmin に従って表示されるようになっています。今回の場合は age フィールドが表示されるようになっているのは CustomUserAdmin をそのように定義したからになります。もし CustumUserAdmin ではなく、UserAdmin をそのまま利用した場合は追加フォームに age フィールドが存在しないため、追加時に例外が発生することになるので注意してください。

CommentAdmin の確認

次は CommentAdmin 側の確認を行なっていきましょう!

まずトップページから Comments リンクの右側にある 追加 リンクをクリックしてください。

クリックすれば追加フォームが表示され Comment のインスタンスの追加が行えることが確認できると思います。

Commentの追加フォーム

このフォームのポイントは、user がプルダウンメニューになっており、そこから CustomUser のインスタンスを選択する形式になっている点になると思います。リレーションを設定するフィールドの場合、このようにプルダウンメニューでインスタンスを選択する形式の入力フィールドが自動的に用意されることになります。

関連付け相手となるCustomUserのインスタンスをプルダウンメニューから選択可能になっている様子

また、インスタンスの追加後に一覧表を表示すれば、下の図のように COMMENT (モデルクラス名) と USER のフィールドが表示されていることが確認できると思います。これも CommentAdmin の定義に従って表示された結果ですね!

Commentのインスタンス一覧表

以上で動作確認は完了となります。いきなりカスタマイズ後の管理画面を利用したのでカスタマイズの効果が分かりにくかったかもしれないですね…。

そういった方は、是非 admin.py を自身で変更してみて、変更前後の管理画面の違いを確認してみていただければと思います。

スポンサーリンク

まとめ

このページでは Django における管理画面のカスタマイズについて解説しました。

デフォルトの管理画面で管理可能なモデルクラスは authUserGroup のみとなりますが、admin.site.register の実行により、自身で定義したモデルクラスも管理画面での管理対象として追加することができます。そして、管理対象として追加されたモデルクラスの管理画面は、基本的に ModelAdmin の定義に従って表示されるようになっています。

また、ModelAdmin のサブクラス を定義することで、各モデルクラスを管理する画面の詳細設定、例えばフォームに表示するフィールドやインスタンスの一覧表に表示するフィールドを追加したり削除したりするようなことも可能となっています。

基本的には、自身で定義したモデルクラスを管理したいだけであれば、ModelAdmin のサブクラス の定義は不要です。この場合、admin.site.register の第1引数に管理したいモデルクラスを定義すれば良いだけなので手順は非常に簡単だと思います。ModelAdmin のサブクラス の定義が必要なのは、管理画面をより使いやすくしたい場合くらいになると思います。

ただし、AbstractUser を継承するモデルを扱う場合は admin.site.register の第2引数に UserAdmin のサブクラス を指定し、ユーザーの扱いに適した管理画面に設定してあげた方が良いです。さらに、UserAbstractUser が持っていないフィールドを追加したりしている場合は、そのフィールドの変更に応じた UserAdmin のサブクラス を定義する必要がある点にも注意してください。UserAdmin はあくまでも User を管理するためのクラスとなります。

この辺りについては下記ページで解説していますので、詳しくは下記ページを参照してください。

管理画面でカスタムユーザーを管理する方法の説明ページアイキャッチ 【Django】管理画面でカスタムユーザーを管理する(UserAdmin)

次の Django 入門の連載では「ページネーション」について解説を行います!下記ページから次の連載を読むことができますので、是非読んでみてください!

Djangoにおけるページネーションの解説ページアイキャッチ 【Django入門13】ページネーションの基本

同じカテゴリのページ一覧を表示