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

This tutorial begins where Tutorial 6 left off. We're continuing the web-poll application and will focus on customizing Django's automatically-generated admin site that we first explored in Tutorial 2.

困ったときは:

このチュートリアルの実行に問題がある場合は、FAQ の Getting Help セクションに進んでください。

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')

念のため、 Tutorial 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 のカラムヘッダは、デフォルトではメソッド名 (アンダースコアを空白に置き換えたもの) になっていることに注目してください。また、各行は戻り値の文字列表現となっています。

これを改善するには、display() デコレータを次のように (polls/models.py の) メソッドに使います:

polls/models.py
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

デコレータで設定できるプロパティに関する詳しい情報は、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 admin はDjango自体を利用しており、そのインターフェースはDjango独自のテンプレートシステムを使用しています。

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

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

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

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

テンプレートの構成

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

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

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

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

$ python -c "import django; print(django.__path__)"
...\> py -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 アプリケーションで登録された INSTALLED_APPS のすべてのアプリケーションが、アルファベット順に表示されます。レイアウトを大きく変更したい場合もあるでしょう。なんといっても、index ページは admin の中でおそらく最も重要なページなので、使いやすいほうがいいですからね。

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

次は何をしましょうか?

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

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