Django の管理サイトをカスタマイズする

DjangoDjangoBeginner
今すぐ練習

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

このチュートリアルは、「スタイルシートと画像の追加」で終わったところから始まります。私たちはウェブ投票アプリケーションを続け、「データベースの設定」で最初に調べたDjangoの自動生成の管理サイトのカスタマイズに焦点を当てます。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL django(("Django")) -.-> django/DatabaseModelsandMigrationsGroup(["Database, Models, and Migrations"]) django(("Django")) -.-> django/UserInterfaceandInteractionGroup(["User Interface and Interaction"]) django(("Django")) -.-> django/DevelopmentandAdministrationToolsGroup(["Development and Administration Tools"]) django/DatabaseModelsandMigrationsGroup -.-> django/models("Models") django/DatabaseModelsandMigrationsGroup -.-> django/databases("Databases") django/DatabaseModelsandMigrationsGroup -.-> django/schemaeditor("SchemaEditor") django/UserInterfaceandInteractionGroup -.-> django/templates("Templates") django/DevelopmentandAdministrationToolsGroup -.-> django/django_admin("Django Admin") django/DevelopmentandAdministrationToolsGroup -.-> django/contrib_packages("Contrib Packages") subgraph Lab Skills django/models -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} django/databases -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} django/schemaeditor -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} django/templates -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} django/django_admin -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} django/contrib_packages -.-> lab-153747{{"Django の管理サイトをカスタマイズする"}} end

管理フォームをカスタマイズする

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」フォームは以下のようになっています。

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」ページを読み込んで、その見た目を確認しましょう。

選択肢付きのQuestion管理

動作原理はこうです。関連する Choice 用のスロットが3つあります(extra によって指定されています)。既に作成済みのオブジェクトの「Change」ページに戻るたびに、追加で3つのスロットが表示されます。

現在の3つのスロットの最後には、「Add another Choice」リンクがあります。これをクリックすると、新しいスロットが追加されます。追加したスロットを削除したい場合は、追加したスロットの右上のXをクリックすることができます。この画像は追加されたスロットを示しています。

動的に追加された追加スロット

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

class ChoiceInline(admin.TabularInline):
  ...

この TabularInlineStackedInline の代わり)を使用すると、関連オブジェクトがよりコンパクトなテーブル形式で表示されます。

テーブル形式のインライン選択肢表示

「Delete?」の追加の列があることに注意してください。これにより、「Add another Choice」ボタンを使用して追加された行や既に保存された行を削除することができます。

管理変更リストをカスタマイズする

これで Question の管理ページが見栄えよくなりましたので、システム内のすべての質問を表示する「変更リスト」ページにいくつかの微調整を加えましょう。

この時点での見た目は以下の通りです。

Polls変更リストページ

デフォルトでは、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"]

これで質問の変更リストページは以下のようになります。

Question変更リストビュー

列ヘッダーをクリックすると、それらの値でソートできます。ただし、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でさらに実験を行うことができます。