【Django】はじめてのDjangoアプリ第13回

django

PythonのWEBフレームワークDjangoを利用したアプリケーションの公式テュートリアルに沿ったWEBアプリケーション作成のメモ第13回です。

Writing your first Django app, part 7 | Django documentation
The web framework for perfectionists with deadlines.

前回は静的ファイルを管理について進めました。

今回は管理サイトのカスタマイズを進めていきます。

adminフォームのカスタマイズ

admin.site.register(Question) を使用して Question モデルを登録することで、Django はデフォルトのフォーム表現を構築できました。管理フォームの外観や動作をカスタマイズしたい場合がよくあります。これを行うには、オブジェクトを登録するときに必要なオプションを Django に伝えます。

編集フォームのフィールドの順序を変更して、これがどのように機能するかを見てみましょう。 admin.site.register(Question) 行を次のように置き換えます。

▼「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 クラスを作成し、それを 2 番目の引数として admin.site.register() に渡します。

上記の特定の変更により、「Date published」が「Question text」フィールドの前に来ます。

フィールドが 2 つだけの場合はそれほど驚くべきことではありませんが、数十のフィールドがある管理フォームの場合、直感的な順序を選択することは重要な使いやすさの詳細です。

また、数十のフィールドを持つフォームについて言えば、フォームをフィールドセットに分割することもできます。

▼「polls/admin.py」を編集

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)

フィールドセット内の各タプルの最初の要素は、フィールドセットのタイトルです。現在のフォームは次のようになります。

関連オブジェクトの追加

OK、質問管理ページはありますが、質問には複数の選択肢があり、管理ページには選択肢が表示されません。

まだ。

この問題を解決するには 2 つの方法があります。 1 つ目は、Question の場合と同様に、Choice を管理者に登録することです。

▼「polls/admin.py」を編集

from django.contrib import admin
from .models import Choice, Question

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"]}),
    ]

admin.site.register(Question, QuestionAdmin)
admin.site.register(Choice)

Django 管理画面で「Choices」が利用できるようになりました。 「Add choice」フォームは次のようになります。

▼サイドメニューに「Choices」が追加されている

※右上の「ADD CHOICE +」ボタンをクリックすると入力フォームに遷移

▼リレーションに基づいて「Question」のプルダウンに質問のテキストが表示され選択できるようになっている

このフォームの「Question」フィールドは、データベース内のすべての質問を含む選択ボックスです。 Django は、ForeignKey が管理画面で選択ボックスとして表される必要があることを認識しています。私たちの場合、この時点で存在する質問は 1 つだけです。

Question」の(右)横にある「Add another question」リンクにも注目してください。別のオブジェクトとのForeignKey関係を持つすべてのオブジェクトは、これを無料で取得します。 [Add another question] をクリックすると、[Add question] フォームが表示されたポップアップ ウィンドウが表示されます。そのウィンドウに質問を追加して [SAVE] をクリックすると、Django は質問をデータベースに保存し、表示されている [Add choice] フォームで選択された選択肢として動的に追加します。

しかし、実際には、これはシステムに Choice オブジェクトを追加する非効率的な方法です。 Question オブジェクトを作成するときに、多数の Choices を直接追加できれば良いでしょう。それを実現しましょう。

Choice モデルの register() 呼び出しを削除します。次に、Question 登録コードを次のように編集します。

▼「polls/admin.py」を編集

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」ページをロードして、どのように表示されるかを確認します。

これは次のように機能します。extra で指定された関連する選択肢には 3 つのスロットがあり、作成済みのオブジェクトの「変更」ページに戻るたびに、さらに 3 つの追加スロットが得られます。

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

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

class ChoiceInline(admin.TabularInline):

(StackedInline の代わりに) TabularInline を使用すると、関連オブジェクトがよりコンパクトな表ベースの形式で表示されます。

余分な「DELETE?」があることに注意してください。 これは「別の選択肢を追加」ボタンを使用して追加された行と、すでに保存されている行を削除できます。

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

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

この時点では次のようになります。

デフォルトでは、Django は各オブジェクトの str() を表示します。ただし、場合によっては、個々のフィールドを表示できた方が便利な場合があります。これを行うには、list_display 管理オプションを使用します。これは、オブジェクトの変更リスト ページに列として表示するフィールド名のリストです。

▼「polls/admin.py」を編集

※「QuestionAdmin()」に「list_display = …」の行を追記

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]
    list_display = ["question_text", "pub_date"]

適切な手段として、チュートリアル 2 の was_published_recently() メソッドも含めてみましょう。

▼「polls/admin.py」を編集

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]
    list_display = ["question_text", "pub_date", "was_published_recently"]

質問変更リスト ページは次のようになります。

列ヘッダーをクリックしてそれらの値で並べ替えることができます。ただし、was_published_recently ヘッダーの場合は例外です。これは、任意のメソッドの出力による並べ替えがサポートされていないためです。また、was_published_recently の列ヘッダーはデフォルトでメソッド名 (アンダースコアがスペースに置き換えられたもの) であり、各行に出力の文字列表現が含まれていることにも注意してください。

次のように、そのメソッド (polls/models.py 内) で display() デコレータを使用することでこれを改善できます。

▼「polls/models.py」を編集

※冒頭に追記

from django.contrib import admin

※「Question()」を編集

※「was_published_recently()」の前の行に「@admin.display()」を追記

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField("date published")
    def __str__(self):
        return self.question_text
    @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

デコレーターを介して構成可能なプロパティの詳細については、「list_display」を参照してください。

polls/admin.py ファイルを再度編集し、質問変更リスト ページに改善を追加します。list_filter を使用してフィルターします。次の行を QuestionAdmin に追加します。

▼「polls/admin.py」を編集

※「list_filter = …」の行を追記

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]
    list_display = ["question_text", "pub_date", "was_published_recently"]
    list_filter = ["pub_date"]

これにより、pub_date フィールドで変更リストをフィルターできる「FILTER」サイドバーが追加されます。

表示されるフィルターの種類は、フィルター対象のフィールドの種類によって異なります。 pub_dateDateTimeField であるため、Django は適切なフィルター オプション (「任意の日付」、「今日」、「過去 7 日間」、「今月」、「今年」) を提供することを認識しています。

これは順調に形になってきています。検索機能を追加しましょう。

▼「polls/admin.py」を編集

※「QuestionAdmin()」に「search_fields = …」の行を追記

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]
    list_display = ["question_text", "pub_date", "was_published_recently"]
    list_filter = ["pub_date"]
    search_fields = ["question_text"]

これにより、変更リストの上部に検索ボックスが追加されます。誰かが検索語を入力すると、Django は question_text フィールドを検索します。フィールドは好きなだけ使用できますが、バックグラウンドで LIKE クエリを使用するため、検索フィールドの数を適切な数に制限すると、データベースでの検索が容易になります。

また、変更リストでは特に設定なしでページネーションができることにも注目してください。デフォルトでは、1 ページあたり 100 個のアイテムが表示されます。変更リストのページネーション、検索ボックス、フィルター、日付階層、列ヘッダーの順序はすべて、思ったとおりに連携して機能します。

adminの見栄えのカスタマイズ

各管理ページの上部に「Django administration」という文字があるのは明らかにばかげています。それは単なるプレースホルダーテキストです。

ただし、Django のテンプレート システムを使用して変更できます。 Django adminは Django 自体を利用しており、そのインターフェイスは Django 独自のテンプレート システムを使用します。

プロジェクトのテンプレートのカスタマイズ

プロジェクト ディレクトリ (manage.py が含まれるディレクトリ) に templates ディレクトリを作成します。テンプレートは、Django がアクセスできるファイル システム上のどこにでも存在できます。 (Django は、サーバーが実行するユーザーとして実行されます。) ただし、テンプレートをプロジェクト内に保持することは、従うべき良い規則です。

設定ファイル (mysite/settings.py) を開き、TEMPLATES 設定に DIRS オプションを追加します。

▼「mysite/settings.py」を編集

※「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 は、Django テンプレートをロードするときにチェックするファイルシステム ディレクトリのリストです。それは検索パスです。

テンプレートの整理

静的ファイルと同じように、すべてのテンプレートを 1 つの大きなテンプレート ディレクトリにまとめて置くと、完全にうまく機能します。ただし、特定のアプリケーションに属するテンプレートは、プロジェクト (templates) ではなく、そのアプリケーションのテンプレート ディレクトリ (polls/templates など) に配置する必要があります。これを行う理由については、再利用可能なアプリのチュートリアルで詳しく説明します。

次に、templates 内に admin というディレクトリを作成し、Django 自体のソース コード (django/contrib/admin/templates) にあるデフォルトの Django 管理テンプレート ディレクトリ内からテンプレート admin/base_site.html をそのディレクトリにコピーします。

Django のソース ファイルはどこにありますか?

システム上の Django ソース ファイルの場所を見つけるのが難しい場合は、次のコマンドを実行します。

python -c "import django; print(django.__path__)"

※筆者の場合、djangoのソースファイルが

~/.pyenv/versions/3.11.9/lib/python3.11/site-packages/django

にあり、その下の

contrib/admin/templates/admin/base_site.html

に対象のテンプレートファイルがあります。

まずは、「polls/templates」フォルダ内に「admin」フォルダを作成し、

そのあとで、上記のテンプレートファイルを作成したフォルダにコピーしてきます。

mkdir polls/templates/admin
cp ~/.pyenv/versions/3.11.9/lib/python3.11/site-packages/django/contrib/admin/templates/admin/base_site.html polls/templates/admin/

次に、ファイルを編集し、{{ site_header|default:_(‘Django Administration’) }} (中括弧を含む) を独自のサイトの名前に置き換えます。次のようなコードセクションが完成するはずです。

▼「polls/templates/admin/base_site.html」を編集

{% extends "admin/base.html" %}

{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}

{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></div>
{% if user.is_anonymous %}
  {% include "admin/color_theme_toggle.html" %}
{% endif %}
{% endblock %}

{% block nav-global %}{% endblock %}

▼adminのページヘッダー部分が「Polls Administration」に変わっている。

このアプローチを使用して、テンプレートをオーバーライドする方法を説明します。実際のプロジェクトでは、おそらく django.contrib.admin.AdminSite.site_header 属性を使用して、この特定のカスタマイズをより簡単に行うことになります。

このテンプレート ファイルには、{% block branding %}{{ title }} などのテキストが多数含まれています。 {% タグと {{ タグは、Django のテンプレート言語の一部です。チュートリアル 3 で見たように、Django が admin/base_site.html をレンダリングするときに、このテンプレート言語が評価されて最終的な HTML ページが生成されます。

Django のデフォルトの管理テンプレートはどれもオーバーライドできることに注意してください。テンプレートをオーバーライドするには、base_site.html で行ったのと同じことを行います。つまり、テンプレートをデフォルトのディレクトリからカスタム ディレクトリにコピーし、変更を加えます。

アプリケーションのテンプレートのカスタマイズ
洞察力のある読者はこう尋ねるでしょう: しかし、DIRS がデフォルトで空だった場合、Django はどのようにしてデフォルトの管理テンプレートを見つけたのでしょうか?その答えは、APP_DIRSTrue に設定されているため、Django はフォールバックとして使用するために、各アプリケーション パッケージ内の templates/ サブディレクトリを自動的に検索するということです (django.contrib.admin がアプリケーションであることを忘れないでください)。

私たちの投票アプリケーションはそれほど複雑ではないため、カスタム管理テンプレートは必要ありません。しかし、より洗練され、機能の一部で Django の標準管理テンプレートの変更が必要になる場合は、プロジェクト内のテンプレートではなく、アプリケーションのテンプレートを変更する方が賢明です。そうすれば、新しいプロジェクトにpollsアプリケーションを含めることができ、必要なカスタム テンプレートを確実に見つけることができます。

Django がテンプレートを見つける方法の詳細については、テンプレートの読み込みに関するドキュメントを参照してください。

同様に、Django admin インデックス ページの外観をカスタマイズすることもできます。

デフォルトでは、管理アプリケーションに登録されている INSTALLED_APPS 内のすべてのアプリがアルファベット順に表示されます。レイアウトに大幅な変更を加えたい場合があります。結局のところ、インデックスはおそらく管理者にとって最も重要なページであり、使いやすいものでなければなりません。

カスタマイズするテンプレートは admin/index.html です。 (前のセクションの admin/base_site.html と同じことを行います。これをデフォルトのディレクトリからカスタム テンプレート ディレクトリにコピーします)。ファイルを編集すると、app_list というテンプレート変数が使用されていることがわかります。この変数には、インストールされているすべての Django アプリが含まれます。これを使用する代わりに、最適と思われる方法でオブジェクト固有の管理ページへのリンクをハードコーディングできます。

管理者に慣れたら、このチュートリアルのパート 8 を読んで、サードパーティ パッケージの使用方法を学習してください。

今回は以上です。次回はサードパーティパッケージの使用を予定しています。

  • 0
  • 0
  • 0
  • 0

コメント

タイトルとURLをコピーしました