はじめての Django アプリ作成、その 7

このチュートリアルは チュートリアル その6 の続きです。ここでは、引き続き Web 投票アプリケーションの開発を続け、 チュートリアル その2 で少し触れた、Django が 自動生成する管理サイトのカスタマイズに焦点を当てます。

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

admin.site.register(Question) の呼び出しによって Question モデルを登録したことで、Django はデフォルトの形式でオブジェクトを表示できました。ふつうは、admin フォームの表示方法や操作の仕方をデフォルトから少し変更したくなりますが、これには、オブジェクトを登録する時にオプションを指定します。

ためしに、編集フォームでのフィールドの並び順を並べ替えてみましょう。 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 のオプションを変更したいときには、モデルごとに admin クラスを作成して、 admin.site.register() の 2 番目の引数に渡すと いうパターンに従ってください。

上の例では、「Publication date」フィールドの表示位置を「Question」フィールドよりも前に変更しています:

Fields have been reordered

二つしかフィールドがないので、あまりぱっとした変化ではありませんね。しかし admin フォームで何ダースものフィールドを操作するような場合には、直感的なフィー ルドの並び順というものはユーザビリティ上重要な要素です。

同じく何ダースもフィールドがある場合、フォームを複数のフィールドセットに分割したいこともあるでしょう。

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)

fieldsets の各タプルの先頭の要素はフィールドセットのタイトルです。 フォームは以下のように表示されます。

Form has fieldsets now

管理サイトのチェンジリストページをカスタマイズする

さあ、これで Question の管理ページはだいぶよくなってきました。今度は「チェンジリスト」ページをすこしいじりましょう。チェンジリスト (change list) は、システム上の全ての Question を表示するページです。

ここでは以下のようになります。

Polls change list page

デフォルトでは、 Django はオブジェクトの str() を表示しますが、各フィールドの値も表示されていると便利でしょう。表示させるには list_display オプションを使います。このオプションには、カラム表示したいフィールドの名前をタプルにして指定します。

polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date')

おまけとして、チュートリアル その2 で定義したカスタムメソッド was_published_recently() も追加してみましょう。

polls/admin.py
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date', 'was_published_recently')

これで、Question のチェンジリストのページは以下のようになります:

Polls change list page, updated

カラムのヘッダをクリックすると、カラムの値に応じてエントリを並べ換えできます。ただし was_published_recently ヘッダは例外で、これはメソッドの戻り値を 使った並べ換えをサポートしていないからです。 was_published_recently のカラムヘッダのデフォルト値がメソッドの名前になっている (アンダースコアは空白に置き換わっている)こと、各行が戻り値の文字列となっていることにも注意して下さい。

これを改善するには、このメソッド (polls/models.py にある) に次のようにいくつかの属性を追加します。

polls/models.py
class Question(models.Model):
    # ...
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

このメソッドのプロパティに関する詳しい情報は、 list_display を参照してください。

polls/admin.py ファイルをもう一度編集して、Question のチェンジリストのページに list_filter を追加して、さらに改良しましょう。それには、QuestionAdmin に次に行を追加します。

list_filter = ['pub_date']

これで、「フィルタ (Filter)」サイドバーができ、チェンジリストを pub_date フィールドの値に従ってフィルタできるようになります。

Polls change list page, updated

フィルタの種類は、フィルタ対象のフィールドの種類に応じて変化します。pub_dateDateTimeField なので、Django はこのフィールドにふさわしいフィルタオプションが、「すべての期間 (“Any date”)」「今日 (“Today”)」「今週 (“Past 7 days”)」「今月 (“This month”)」であることを知っているのです。

細工は隆々ですね。今度は検索機能を追加してみましょう。

search_fields = ['question_text']

これで変更リストの上部に検索ボックスが表示されます。ユーザが検索語を入力すると、 Django は question_text フィールドを検索します。フィールドはいくらでも使えますが、舞台裏では LIKE クエリを使うのでデータベースに過剰な負荷をかけないために常識的な範囲にしましょう。

さて、変更リストには何もしなくてもページ分割機能がある、ということをここでお知らせしておいた方がよいでしょう。デフォルトではページあたり 100 個の要素を表示します。ページ分割検索ボックスフィルタ日付による階層化カラムヘッダを使った並び替え チェンジリストの機能は、すべて協調して思いのままに動作します。

管理サイトのルック & フィールをカスタマイズする

管理サイトの上部には「Django 管理 (Django adminstration)」と表示されていますが、これはいささか滑稽ですね。これは単なるプレースホルダテキストにすぎません。

変更するのは簡単で、 Django のテンプレートシステムを使います。 Django の管理サイトは、それ自身 Django で作られているので、インタフェースは Django のテンプレートシステムを使っています。

プロジェクト テンプレートをカスタムする。

templates ディレクトリをプロジェクトディレクトリ (manage.py が置かれているディレクトリ) に作成してください。テンプレートは Django がアクセスできるファイルシステム内のどこに置いても構いません (アプリケーションサーバーを実行したユーザーでアクセスします)。しかし従うべき慣習として、テンプレートはプロジェクト内に置きましょう。

設定ファイル (mysite/settings.py, remember) を開いて、TEMPLATES 設定オプションの中に、次のように DIRS オプションを追加します。

mysite/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(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 がテンプレートを読み込む時にチェックする、ファイルシステム上のディレクトリのリストです。サーチパスのようなものです。

テンプレートの構成

static ファイルと同じように、すべてのテンプレートを1つの大きなテンプレートディレクトリの中に置くことも可能ではあります。しかし、到底のアプリケーションに属するテンプレートは、プロジェクトのテンプレートディレクトリ(templates)ではなく、各アプリケーションのテンプレートディレクトリ(例: polls/templates)に配置するべきです。 再利用可能なアプリチュートリアル では、そのようにすべき理由を説明しています。

さて、templates の中に admin という名前のディレクトリを作りましょう。Django 自体のソースコード内にある、デフォルトの Django admin テンプレートディレクトリ (django/contrib/admin/templates) を探して、 admin/base_site.html というテンプレートを、新しく作ったディレクトリにコピーします。

Django のソースファイルの場所はどこ?

Django のソースファイルがシステム中のどこにあるのか分からない場合は、以下のコマンドを実行してください。

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

あとは、ファイルを編集して、{{ site_header|default:_('Django administration') }} (カーリーブラケットも含みます) を適当な自分自身のサイト名に書き換えるだけです。コードは最終的に次のようになります。

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}

他のテンプレートのオーバーライドも、このような方法で行います。実際のプロジェクトで今行ったのと同じカスタマイズを行いたい場合には、django.contrib.admin.AdminSite.site_header 属性を使えば、もっと簡単に設定できます。

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

Django のデフォルトの admin 用テンプレートは上書き可能です。上書きするには、ただ base_site.html にしたことと同じことをしてください。デフォルトのディレクトリからカスタム用のディレクトリにコピーして、変更してください。

アプリケーション用の テンプレートをカスタマイズする

するどい読者はこう質問されるでしょう: DIRS はデフォルトで空っぽなのに、Djangoはなぜデフォルトのadminテンプレートを見つけることができたのだろう? その答えは:setting:APP_DIRS <TEMPLATES-APP_DIRS>True に設定されているため、Djangoは自動的に各アプリケーションのパッケージのサブディレクトリからフォールバックとして``templates/を探すからです(``django.contrib.admin は一つのアプリケーションだということを忘れないでください)。

投票アプリケーションはそれほど複雑ではないので、カスタムのアドミンテンプレートは必要ないでしょう。しかしより凝ったものに成長したり、機能のため標準の admin テンプレートの変更は必要となった場合、 アプリケーションの テンプレートを編集するのは、 プロジェクト のそれを編集するより賢い選択になるでしょう。それにより、投票アプリケーションを他のどの新しいプロジェクトに追加できます。そのアプリが必要としているカスタムテンプレートを見つけることも見つけることもできます。

Django のテンプレートの検索方法に関する詳しい情報は、テンプレート読み込み ドキュメント を参照してください。

admin index ページをカスタムする

近い話題として、 Django の admin index ページのルックアンドフィールをカスタマイズもできます。

デフォルトでは、admin アプリケーションに登録されている :settings:`INSTALLED_APPS` のすべてのアプリケーションが、アルファベット順に表示されてしまいまうので、レイアウトを大きく変更したくなることがあるでしょう。なんといっても、index ページはおそらく admin の中でも最も重要なページなので、使いやすくあるべきですからね。

カスタマイズ対象のテンプレートは “admin/index.html” です。 (前セクションで “admin/base_site.html” にしたことと同じことをしてください。デフォルトのディレクトリからコピーして、カスタムテンプレートのディレクトリに配置してください)。編集してみると “app_list” というテンプレート変数が使われているのがわかるでしょう。この変数にはインストールされた全 Django アプリが含まれています。これを使う代わりに、オブジェクトごとの管理ページヘのリンクをハードコードすることができます。

次は何をしましょうか?

これで、初めての人のためのチュートリアルはおしまいです。次のステップへ では、ここから進める場所について説明しています。

Python のパッケージングについて知っていて、polls アプリケーションを「再利用可能なアプリケーション」へと変える方法について学びたい場合は、チュートリアル 応用編: 再利用可能なアプリケーションの書き方 を読んでみてください。

Back to Top