はじめに
このチュートリアルは、「スタイルシートと画像の追加」で終わったところから始まります。私たちはウェブ投票アプリケーションを続け、「データベースの設定」で最初に調べた Django の自動生成の管理サイトのカスタマイズに焦点を当てます。
管理フォームをカスタマイズする
admin.site.register(Question) で Question モデルを登録することで、Django はデフォルムの表現を構築することができました。多くの場合、管理フォームの見た目や動作をカスタマイズしたいと思うでしょう。これは、オブジェクトを登録する際に Django に望むオプションを伝えることで行います。
編集フォームのフィールドの順序を変更することで、これがどのように機能するか見てみましょう。admin.site.register(Question) 行を以下のものに置き換えます。
~/project/mysite/polls/admin.py ファイルを編集して、以下のようになるようにします。
from django.contrib import admin
from.models import Question
class QuestionAdmin(admin.ModelAdmin):
fields = ["pub_date", "question_text"]
admin.site.register(Question, QuestionAdmin)
このパターンに従います。モデル管理クラスを作成し、それを admin.site.register() の 2 番目の引数として渡します。モデルの管理オプションを変更する必要があるときはいつでもこれを行います。
Django 開発サーバーを起動します。
cd ~/project/mysite
python manage.py runserver
デスクトップ環境の Firefox で http://127.0.0.1:8000/admin/ を開き、「Questions」リンクをクリックします。以下のようなフォームが表示されるはずです。
上記の特定の変更により、「Publication date」が「Question」フィールドの前に表示されるようになります。

2 つのフィールドだけでは印象的ではありませんが、数十個のフィールドを持つ管理フォームの場合、直感的な順序を選ぶことは重要なユーザビリティの詳細です。
そして、数十個のフィールドを持つフォームについて言えば、フォームをフィールドセットに分割したい場合があります。
from django.contrib import admin
from.models import Question
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {"fields": ["question_text"]}),
("Date information", {"fields": ["pub_date"]}),
]
admin.site.register(Question, QuestionAdmin)
~django.contrib.admin.ModelAdmin.fieldsets の各タプルの最初の要素は、フィールドセットのタイトルです。これが現在のフォームの見た目です。

関連オブジェクトの追加
さて、私たちは Question の管理ページを持っていますが、Question には複数の Choice があり、管理ページには選択肢が表示されません。
まだです。
この問題を解決する方法は 2 つあります。最初の方法は、Question の場合と同じように、管理サイトに Choice を登録することです。
from django.contrib import admin
from.models import Choice, Question
#...
admin.site.register(Choice)
今、「Choices」は Django 管理サイトで利用可能なオプションになりました。「Add choice」フォームは以下のようになっています。

そのフォームで、「Question」フィールドはデータベース内のすべての質問を含むセレクトボックスです。Django は、~django.db.models.ForeignKey が管理サイトで <select> ボックスとして表されるべきであることを知っています。私たちの場合、この時点では 1 つの質問のみが存在します。
また、「Question」の横にある「Add another question」リンクにも注目してください。他のオブジェクトと ForeignKey 関係を持つすべてのオブジェクトには、これが自動的に付きます。「Add another question」をクリックすると、「Add question」フォームが表示されるポップアップウィンドウが表示されます。そのウィンドウで質問を追加して「Save」をクリックすると、Django は質問をデータベースに保存し、表示している「Add choice」フォームで選択された選択肢として動的に追加します。
しかし、本当に言えば、これはシステムに Choice オブジェクトを追加する非効率的な方法です。Question オブジェクトを作成する際に、一括で複数の Choice を追加できる方が良いでしょう。それを実現しましょう。
Choice モデルの register() 呼び出しを削除します。そして、Question の登録コードを以下のように編集します。
from django.contrib import admin
from.models import Choice, Question
class ChoiceInline(admin.StackedInline):
model = Choice
extra = 3
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {"fields": ["question_text"]}),
("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
]
inlines = [ChoiceInline]
admin.site.register(Question, QuestionAdmin)
これは Django に対して、「Choice オブジェクトは Question の管理ページで編集されます。デフォルトでは、3 つの選択肢用のフィールドを用意します。」と伝えています。
「Add question」ページを読み込んで、その見た目を確認しましょう。

動作原理はこうです。関連する Choice 用のスロットが 3 つあります(extra によって指定されています)。既に作成済みのオブジェクトの「Change」ページに戻るたびに、追加で 3 つのスロットが表示されます。
現在の 3 つのスロットの最後には、「Add another Choice」リンクがあります。これをクリックすると、新しいスロットが追加されます。追加したスロットを削除したい場合は、追加したスロットの右上の X をクリックすることができます。この画像は追加されたスロットを示しています。

ただし、1 つ小さな問題があります。関連する Choice オブジェクトを入力するためのすべてのフィールドを表示するには、画面領域が多く必要になります。そのため、Django はインライン関連オブジェクトを表示するテーブル形式を提供しています。それを使用するには、ChoiceInline の宣言を以下のように変更します。
class ChoiceInline(admin.TabularInline):
...
この TabularInline(StackedInline の代わり)を使用すると、関連オブジェクトがよりコンパクトなテーブル形式で表示されます。

「Delete?」の追加の列があることに注意してください。これにより、「Add another Choice」ボタンを使用して追加された行や既に保存された行を削除することができます。
管理変更リストをカスタマイズする
これで Question の管理ページが見栄えよくなりましたので、システム内のすべての質問を表示する「変更リスト」ページにいくつかの微調整を加えましょう。
この時点での見た目は以下の通りです。

デフォルトでは、Django は各オブジェクトの str() を表示します。しかし、個々のフィールドを表示できる方が時には便利です。そのためには、~django.contrib.admin.ModelAdmin.list_display 管理オプションを使用します。これは、オブジェクトの変更リストページに列として表示するフィールド名のタプルです。
class QuestionAdmin(admin.ModelAdmin):
#...
list_display = ["question_text", "pub_date"]
念のため、「データベースの設定」の was_published_recently() メソッドも含めましょう。
class QuestionAdmin(admin.ModelAdmin):
#...
list_display = ["question_text", "pub_date", "was_published_recently"]
これで質問の変更リストページは以下のようになります。

列ヘッダーをクリックすると、それらの値でソートできます。ただし、was_published_recently ヘッダーの場合は例外です。なぜなら、任意のメソッドの出力でソートすることはサポートされていないからです。また、was_published_recently の列ヘッダーは、デフォルトではメソッド名(アンダースコアがスペースに置き換えられています)であり、各行には出力の文字列表現が含まれていることにも注意してください。
それを改善するには、そのメソッド(polls/models.py 内)に ~django.contrib.admin.display デコレータを使用します。以下のようになります。
from django.contrib import admin
class Question(models.Model):
#...
@admin.display(
boolean=True,
ordering="pub_date",
description="Published recently?",
)
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
デコレータを介して設定可能なプロパティに関する詳細は、~django.contrib.admin.ModelAdmin.list_display を参照してください。
もう一度 polls/admin.py ファイルを編集し、Question の変更リストページに改善を加えましょう。~django.contrib.admin.ModelAdmin.list_filter を使用したフィルタリングです。QuestionAdmin に以下の行を追加します。
list_filter = ["pub_date"]
これにより、「Filter」サイドバーが追加され、pub_date フィールドで変更リストをフィルタリングできるようになります。

表示されるフィルタの種類は、フィルタリング対象のフィールドの種類に依存します。pub_date は ~django.db.models.DateTimeField なので、Django は適切なフィルタオプションを与えることが知っています。「Any date」、「Today」、「Past 7 days」、「This month」、「This year」です。
これはうまくいっています。検索機能を追加しましょう。
search_fields = ["question_text"]
これにより、変更リストの上部に検索ボックスが追加されます。誰かが検索用の用語を入力すると、Django は question_text フィールドを検索します。好きなだけ多くのフィールドを使用できます。ただし、内部的に LIKE クエリを使用するため、検索フィールドの数を合理的な数に制限することで、データベースが検索を行うのが容易になります。
この時点で、変更リストには無料でページネーションが備え付けられていることにも注目してください。デフォルトは 1 ページに 100 件の項目を表示することです。変更リストのページネーション <django.contrib.admin.ModelAdmin.list_per_page>、検索ボックス <django.contrib.admin.ModelAdmin.search_fields>、フィルタ <django.contrib.admin.ModelAdmin.list_filter>、日付階層 <django.contrib.admin.ModelAdmin.date_hierarchy>、および 列ヘッダーの並び替え <django.contrib.admin.ModelAdmin.list_display> はすべて、思った通りに機能します。
管理画面の見た目をカスタマイズする
明らかに、各管理画面の上部に「Django administration」があるのは不合理です。これはただの置き換えテキストにすぎません。
ただし、Django のテンプレートシステムを使用して変更することができます。Django 管理画面は Django 自身によって動作し、そのインターフェイスは Django 自身のテンプレートシステムを使用しています。
プロジェクトのテンプレートをカスタマイズする
プロジェクトディレクトリ(manage.py が含まれているディレクトリ)に templates ディレクトリを作成します。テンプレートは、Django がアクセスできるファイルシステムのどこにでも置くことができます。(Django はサーバーが実行するユーザーとして実行されます。)ただし、プロジェクト内にテンプレートを置くことは、従うべき良い慣例です。
設定ファイル(mysite/settings.py、覚えていますか?)を開き、TEMPLATES 設定に DIRS <TEMPLATES-DIRS> オプションを追加します。
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "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",
],
},
},
]
DIRS <TEMPLATES-DIRS> は、Django テンプレートを読み込む際にチェックするファイルシステムディレクトリのリストです。これは検索パスです。
テンプレートの整理
静的ファイルと同じように、すべてのテンプレートを 1 つの大きな templates ディレクトリにまとめておけば、完全に機能します。ただし、特定のアプリケーションに属するテンプレートは、そのアプリケーションのテンプレートディレクトリ(たとえば polls/templates)に配置する方が良いでしょう。これについては、再利用可能なアプリケーションのチュートリアル </intro/reusable-apps> で詳細に説明します。
次に、templates 内に admin というディレクトリを作成し、Django 自体のソースコード内のデフォルトの Django 管理テンプレートディレクトリ(django/contrib/admin/templates)からテンプレート admin/base_site.html をコピーしてそのディレクトリに貼り付けます。
Django のソースファイルはどこにあるのか?
システム上の Django のソースファイルがどこにあるかを見つけるのが難しい場合は、次のコマンドを実行します。
python -c "import django; print(django.__path__)"
['/home/labex/.local/lib/python3.10/site-packages/django']
そして、ファイルを編集して、{{ site_header|default:_('Django administration') }}(波括弧も含めて)を適切なサイト名に置き換えます。最終的には次のようなコードセクションになるはずです。
{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a><div>
{% endblock %}
このアプローチを使用して、テンプレートをオーバーライドする方法を学びます。実際のプロジェクトでは、この特定のカスタマイズをより簡単に行うために、django.contrib.admin.AdminSite.site_header 属性を使用する場合が多いでしょう。
このテンプレートファイルには、{% block branding %} や {{ title }} のようなテキストがたくさん含まれています。{% と {{ タグは Django のテンプレート言語の一部です。Django が admin/base_site.html をレンダリングするとき、このテンプレート言語が評価されて最終的な HTML ページが生成されます。これは、**パブリックインターフェイスビューの作成** で見たのと同じです。
Django のデフォルトの管理テンプレートのいずれもオーバーライドできることに注意してください。テンプレートをオーバーライドするには、base_site.html と同じことを行います。デフォルトのディレクトリからコピーしてカスタムディレクトリに貼り付け、変更を加えます。
アプリケーションのテンプレートをカスタマイズする
鋭い読者は次のように尋ねるかもしれません。では、DIRS <TEMPLATES-DIRS> がデフォルトで空だった場合、Django はどのようにしてデフォルトの管理テンプレートを見つけたのでしょうか?答えは、APP_DIRS <TEMPLATES-APP_DIRS> が True に設定されているため、Django は各アプリケーションパッケージ内の templates/ サブディレクトリを自動的に探し、フォールバックとして使用します(忘れないでください。django.contrib.admin はアプリケーションです)。
私たちの投票アプリケーションはあまり複雑ではなく、カスタム管理テンプレートが必要ありません。ただし、もっと洗練され、一部の機能に対して Django の標準的な管理テンプレートを変更する必要がある場合、プロジェクトのテンプレートではなく、アプリケーションのテンプレートを変更する方が賢明です。そうすることで、投票アプリケーションを新しいプロジェクトに含めることができ、必要なカスタムテンプレートを見つけることができます。
Django がテンプレートを見つける方法に関する詳細は、テンプレートの読み込みドキュメント <template-loading> を参照してください。
管理インデックスページをカスタマイズする
同様に、Django 管理インデックスページの見た目をカスタマイズしたい場合があります。
デフォルトでは、管理アプリケーションに登録されている INSTALLED_APPS 内のすべてのアプリケーションがアルファベット順に表示されます。レイアウトに大きな変更を加えたい場合があります。結局のところ、インデックスはおそらく管理画面で最も重要なページであり、使いやすいものにする必要があります。
カスタマイズするテンプレートは admin/index.html です。(前節の admin/base_site.html と同じことを行います。デフォルトのディレクトリからカスタムテンプレートディレクトリにコピーします。)ファイルを編集すると、app_list と呼ばれるテンプレート変数が使用されていることがわかります。この変数には、インストールされているすべての Django アプリケーションが含まれています。それを使用する代わりに、オブジェクト固有の管理ページへのリンクを、あなたが最善と思う方法でハードコードすることができます。
まとめ
おめでとうございます!あなたは Django の管理サイトのカスタマイズの実験を完了しました。あなたのスキルを向上させるために、LabEx でさらに実験を行うことができます。