はじめに
このチュートリアルは、「スタイルシートと画像の追加」で終わったところから始まります。私たちはウェブ投票アプリケーションを続け、「データベースの設定」で最初に調べたDjangoの自動生成の管理サイトのカスタマイズに焦点を当てます。
このチュートリアルは、「スタイルシートと画像の追加」で終わったところから始まります。私たちはウェブ投票アプリケーションを続け、「データベースの設定」で最初に調べた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のソースファイルがどこにあるかを見つけるのが難しい場合は、次のコマンドを実行します。
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でさらに実験を行うことができます。