自己紹介

横瀬明仁氏:今回話すのは、「管理サイトをカスタマイズするにあたって苦労するポイントとその解決策」についてです。使っているDjangoのバージョンは、現時点で最新の3.2を前提としています。

それでは目次です。ボリュームがたくさんあるので、お笑い要素はかなり少なめですがご容赦ください。

自己紹介です。横瀬明仁と言います。Django界隈では“akiyoko”(という名前)で活動していることが多いので、知っている方もいるかもしれません。

前回のDjangoCongressでは、「現場で使えるDjangoのセキュリティ対策」というテーマで発表しました。そのへんも興味ある方は検索してみてください。Djangoの本も4冊出しているので、そちらで知っている方もいるかもしれません。

Django管理サイトのカスタマイズ系の困りごと

みなさん、Django管理サイトは使っていますでしょうか?今日はオンライン登壇のため様子が見えないので、こんなこともあろうかと、私が2020年8月に取ったアンケートの結果を用意しました。これによると、約9割の方が「いつも使っている」あるいは「たまに使っている」と回答していました。

海外の「Django Developers Community Survey 2020」という4,000名を超える開発者のアンケートでも、Django管理サイトが最も利用価値の高い組み込みアプリケーションに選出されていて、開発者からの評価も高いです。

私のアンケートに戻りますが、「どんなところにDjango管理サイトのメリットを感じますか?」という質問をしたところ、1位と2位に「手軽で便利に使える」ところがメリットとして挙げられていました。

一方で、「Django管理サイトのデメリットを挙げるとすればどんなものがありますか?」と聞いたところ、カスタマイズ系の困りごとが多いことがわかりました。

カスタマイズ系の困りごとをまとめると、5つぐらいに整理されると思います。1番目が、どんなカスタマイズが簡単にできるのかわからない。2番目が、テンプレートを修正するのにコツがいる。3番目が、画面のスタイルを変えるのが大変。4番目が、コードが断片化しやすくてテストしづらい。5番目が、日本語の情報が少ない。これらをすべて解決していきたいと思います。

そもそもDjango管理サイトとは何か?

その前に、「Django管理サイトとは何か?」について軽く説明したいと思います。Django管理サイトは、一言で言うとモデルに対応したレコードのCRUD(Create、Read、Update、Delete)画面を提供してくれる、Djangoの組み込みアプリケーションです。

(スライドを指して)右側はひな型プロジェクトのsettings.pyです。INSTALLED_APPSの1つ目に加わっている、django.contrib.adminがDjango管理サイトになります。これはデフォルトで有効化されているということになりますね。

次に基本仕様として、画面遷移の説明をします。ほかにも細かい画面遷移はありますが、(スライドを示し)ざっくりこんな感じになっています。専用のログイン画面があり、ログインするとホーム画面。そして、モデル一覧やモデルごとの一覧画面、追加画面、変更画面、削除確認画面。すなわちCRUD画面がそれぞれモデルごとに用意されています。

ログイン条件ですが、Django管理サイトではデフォルトでユーザーモデルが使われます。ただ、カスタムユーザーモデルの場合もあります。そのユーザーモデルにおいて、「is_staffとis_activeの属性がいずれもTrueであること」がログイン条件になります。この条件はカスタマイズできます。それについてはのちほど紹介します。

最後にパーミッションの説明です。これは非常に重要な仕組みです。モデルのCRUDを実行するためには、モデルごと・操作ごとの“パーミッション”と呼ばれる権限が必要です。

例えばBookモデルなら、それぞれ追加・変更・削除・参照というパーミッションが用意されていて、Bookモデルの追加操作をしたければ、Bookモデルの追加パーミッションが、事前にユーザーモデル、つまりユーザーにひもづける事前準備が必要です。

(スライドを指して)ここに示したように、ユーザーの追加画面と変更画面でユーザーパーミッションとしてひもづけることができます。ちなみにis_superuserがTrueのユーザーは、すべてのパーミッションがあるとみなされる仕組みになっています。

ここまでがDjango管理サイトの基本仕様の話でした。それでは、Django管理サイトのカスタマイズをするにあたって、苦労するポイントとその解決策に戻りたいと思います。

Django管理サイトの全体構造

困りごとの1番目は、「どんなカスタマイズが簡単にできるのかわからない」という悩みでした。

Django管理サイトをカスタマイズする前に知っておきたいこととして、まずは全体構造を理解する必要があります。全体構造は、AdminSiteクラスとModelAdminクラスのことです。「なんじゃそりゃ、聞いたことがないぞ」という方もいるかもしれませんが、ちょっと思い出してみてください。

(スライドを指して)この左側のコードが、startprojectコマンドで作成したひな型プロジェクトのurls.pyです。このadmin.siteというのが、実はAdminSiteクラスのグローバルオブジェクトです。DjangoがWebアプリケーションとして起動する際、真っ先にこのAdminSiteクラスがインスタンス化されて、admin.siteという名前で参照できるようになっています。

一方で、右側のものはよくあるadmin.pyの例です。最後の行にあるadmin.siteで、先ほど説明したAdminSiteクラスのグローバルオブジェクトであるregisterメソッドを使い、第1引数にBookクラスを指定することで、BookクラスをDjango管理サイトに登録しています。

この時に第2引数を指定しなければ、素のデフォルトのModelAdminが使われます。また、このように第2引数に定義してあければ、派生クラスを定義してカスタマイズできるようになっています。

このように、Django管理サイトの全体構造はAdminSiteとModelAdminで、いずれもURLConfとViewを兼ね備えたようなクラスになっています。Django管理サイト内の動的なURLパターンと、それにひもづいたView用のメソッドを持っています。そのため、「/admin/…」のURLパターンが来た場合にはAdminSiteが対応して、ModelごとのURLが来た場合にはモデルごとのModelAdminが対応する仕組みになっています。

そして、AdminSiteとModelAdminには、カスタマイズするためのクラス変数やメソッドがいくつか用意されています。全体の画面項目や挙動を変える時はAdminSite、モデルごとの画面項目や挙動を変える時はModelAdminクラスに定義することで、容易にカスタマイズできます。

以上を踏まえて、カスタマイズの方針を考える時、まずはAdminSiteやModelAdminを利用してカスタマイズするほうが、オーバーライドできるクラス変数やメソッドが用意されているので、比較的簡単になります。

一方で、コツが少し必要な難しいカスタマイズとして、テンプレートやCSSをカスタマイズするのは、ちょっとコツが必要です。これはポイント2と3で説明していきます。

AdminSiteを利用してカスタマイズする方法

では1つ目。AdminSiteを利用して、全体のカスタマイズをする方法について説明します。(スライドを指して)AdminSiteでカスタマイズ可能なクラス変数のうち、主要なものをピックアップしました。これらについて一つひとつ説明するというよりは、「ふぅん、こんなカスタマイズができるんだな」という感じで、ざっと眺めてもらえればよいと思います。

次に、AdminSiteでカスタマイズできる主要なメソッドを抜粋しました。

これを使って、どうやってカスタマイズをするかを具体的に見せます。(スライドを指して)この例のように、ルートURLConfという設定ディレクトリの中にurls.pyがあるのですが、そこでAdminSiteの属性をダイレクトに書き換えてしまうのが、一番手っ取り早いです。

この例では、ヘッダーの左側のサイト名をsite.site_headerで書き換えたり、あとはtitleタグの一部をsite_titleで書き換えたり、ヘッダーの右側にリンクがありますが、このsite_urlをNoneにすることで非表示にしたりできます。あるいは、site.has_permissionというメソッドがあるのですが、これを差し替えてDjango管理サイトのログイン条件を変更することもできます。

ちなみに、今回はAdminSiteの属性をダイレクトに書き換えた例を示しましたが、AdminSite派生クラスを作成して、それを起動時に読み込ませることも可能です。

ModelAdminを利用してカスタマイズする方法

次に2つ目。ModelAdminを利用してカスタマイズする方式です。主にモデル一覧画面で利用できる、ModelAdminの主要なクラス変数を抜粋しました。モデル一覧画面は、バラエティ豊富なカスタマイズができるので、このくらいは把握しておくことをおすすめします。あとでいくつか例を挙げて説明します。

次に、モデル追加・変更画面で主に使うクラス変数です。あとでじっくり見てもらうとして、次に進みます。

これがModelAdminでカスタマイズできる主要なメソッド一覧です。Django管理サイトの挙動を変更できます。

実際にカスタマイズ例を挙げて説明します。まずはモデル一覧画面の画面表示フィールドをカスタマイズするための、list_displayというクラス変数です。admin.pyでModelAdminを継承した派生クラスを作って、そのクラス変数list_displayにモデルのフィールド名を列挙します。

そして、registerの第2引数に指定することでカスタマイズできます。デフォルトでは、モデルの__str__(ダンダーストラ)メソッドの列しか出ていないのですが、これをカスタマイズできます。

次にsearch_fieldsです。これがモデル一覧画面に簡易的な検索機能を追加してくれる、カスタマイズ可能なクラス変数になります。このsearch_fieldsにモデルのどのフィールドを検索条件に含めるかを列挙します。

検索フィールドのワードは、スペース区切りで入れることで、and条件で検索できます。また、この例で示したように、ForeignKeyやManyToManyフィールドのフィールド名と、その関連先のモデルのフィールド名をアンダーバー2つでつないで指定することもできます。これはフォロー記法と言います。

(スライドを指して)この場合だと、Bookモデル・ForeignKeyで関連しているPublisherモデルのnameを検索できるようになっています。

続いてlist_filterは、画面の右側に、フィルターと呼ばれる絞り込みのUIを追加してくれるクラス変数です。この例のように、モデルのフィールドをlist_filterに列挙するだけで、フィールドの種類に応じたフィルターが追加されます。

例えばこれはモデルにChoiceField、choicesオプションを付けた例ですが、それを自動的に候補としてつけてくれます。

続いてアクション一覧があり、このモデル一覧画面の上側には、「操作」というラベルがついたプルダウンリストがあります。これはチェックボックスを入れたレコードに対して、一括操作ができるような仕組みになっていて、先ほどパーミッションのところで説明した削除パーミッションがある場合は、ここに「○○モデルの削除」というプルダウンが出てきます。それをチェックすれば、一括削除ができるようになっています。actionsに関数やメソッドを追加すれば、ここに表示できるようになります。

この例では、publish_todayというレコードの出版日をアップデートするメソッドを追加しています。このactionsを使えば、簡単に選択したレコードをCSVダウンロードする機能も簡単に追加できます。ちなみに、Django 3.2で追加されたこのactionデコレータを使って、画面に表示する文字列や操作するアクションを指定できます。

最後はフォームです。モデル追加・変更画面のフォームを変更したい場合は、クラス変数のformに、新しく自分で作ったフォームを指定します。このModelFormを継承したフォームを追加するのがポイントです。

バリデートを追加するパターンですが、例えばclean_priceを作って、「価格が1万円以上にならないように」みたいなバリデーションチェックをDjango管理サイトに追加できます。

AdminSiteやModelAdminを活用したカスタマイズから始めるのがおすすめ

ここでポイント1のまとめです。「どんなカスタマイズが簡単にできるのかわからない」という問題については、AdminSiteやModelAdminのクラス変数やメソッドを利用して、特定の画面の項目や挙動をカスタマイズするのが一番簡単です、というのが回答になります。

なのでまずは、AdminSiteやModelAdminのクラス変数やメソッドを利用してカスタマイズするところから始めるとよいと思います。

(次回に続く)