はじめての 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) を開いて、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 がテンプレートを読み込む時にチェックする、ファイルシステム上のディレクトリのリストです。サーチパスのようなものです。

テンプレートの構成

Just like the static files, we could have all our templates together, in one big templates directory, and it would work perfectly well. However, templates that belong to a particular application should be placed in that application’s template directory (e.g. polls/templates) rather than the project’s (templates). We’ll discuss in more detail in the reusable apps tutorial why we do this.

さて、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 テンプレートを見つけることができたのだろう? その答えは APP_DIRSTrue に設定されているため、 Django は自動的に各アプリケーションのパッケージのサブディレクトリからフォールバックとして templates/ を探すからです (django.contrib.admin は一つのアプリケーションだということを忘れないでください)。

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

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

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

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

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

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

次は何をしましょうか?

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

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

Back to Top