---
title: "はじめての Django アプリ作成、その 7"
version: 1.10
locale: ja
source: https://docs.djangoproject.com/ja/1.10/intro/tutorial07/
canonical: https://djangodocs.dev/ja/1.10/intro/tutorial07/
---
# はじめての Django アプリ作成、その 7

このチュートリアルは [チュートリアル その6](/ja/1.10/intro/tutorial06/) の続きです。ここでは、引き続き Web 投票アプリケーションの開発を続け、 [チュートリアル その2](/ja/1.10/intro/tutorial02/) で少し触れた、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](intro/_images/admin07.png)

二つしかフィールドがないので、あまりぱっとした変化ではありませんね。しかし 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`](/ja/1.10/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets) の各タプルの先頭の要素はフィールドセットのタイトルです。 フォームは以下のように表示されます。

![Form has fieldsets now](intro/_images/admin08t.png)

## リレーションを張ったオブジェクトの追加

OK、 Question の管理ページはできました。しかし `Question` は複数の `Choice`s を持つのに、管理ページには表示されていませんね。

今はまだ。

この問題の解決法は二つあります。一つ目は、 `Question` と同様、以下のようにして `Choice` モデルを管理サイトに登録するというものです。これは簡単です。

*polls/admin.py*

```
from django.contrib import admin

from .models import Choice, Question
# ...
admin.site.register(Choice)
```

これで、 Django の管理サイト上で「Choice」 を選べます。「Choice の追加」フォーム は以下のようになります。

![Choice admin page](intro/_images/admin09.png)

このフォームでは「Question」フィールドは選択ボックスで、データベース上の全ての質問を選択できます。Django は [`ForeignKey`](/ja/1.10/ref/models/fields/#django.db.models.ForeignKey) を表示する時には `<select>` ボックスを使わねばならないということを知っているのです。今の時点では、 Question はデータベース上に一つしかないはずですね。

Question フィールドの隣に「もう一つ追加 (Add Another)」リンクがあるのに注意してください。 `ForeignKey` の関係にあるオブジェクトなら、何もしなくてもこのリンクが表示されます。「もう一つ追加」をクリックすると、「Poll を追加 (Add poll)」というポップアップウィンドウを表示します。このウィンドウで Poll を追加して「保存」を押すと、 Django は Poll をデータベースに保存して、もとの 「Choice の追加」フォームに選択済みの項目として動的に追加します。

しかし、この方法は `Choice` オブジェクトをシステムに追加するには効率的ではありません。 `Question` オブジェクトを追加する時に Choice をひと揃い追加できた方が便利ですよね。そうしてみましょう。

`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 つの `Choice` を表示するのに十分なフィールドを用意すること」と指示しています。

「questionを追加 (Add question)」ページを読み込んで、どのように見えるか確かめてみましょう。

![Add question page now has choices on it](intro/_images/admin10t.png)

変わった点をみてみましょう。リレーション相手である `Choice` を表示するために 3 つのスロットがあります (`extra` に指定した数ですね)。また、作成済みのオブジェクトを「編集する」ページに行けば、いつでも 3 つ余分のスロットが表示されるはずです。

現状ある3つのスロットの末尾に、「他の Choice を追加する (Add another Choice)」リンクがあります。クリックすると、新しいプロットが追加されます。追加されたスロットを削除したい場合は、追加されたスロット右上の 'X' をクリックしてください。最初からある 3 つのスロットを削除できないことに注意してください。この画像は追加されたスロットのみで有効です。

![Additional slot added dynamically](intro/_images/admin14t.png)

さて、このコードにはちょっと問題があります。というのも、 `Choice` オブジェク トを入力するためのフィールドを全部表示しようとすると、相当な広さのスクリーンが必要だからです。そこで、 Django にはテーブルを使ってインラインでリレーション相手のオブジェクトを表示するもう一つの方法があります。以下のように、 `ChoiceInline` の宣言を変更してください。

*polls/admin.py*

```
class ChoiceInline(admin.TabularInline):
    #...
```

`StackedInline` に変わって `TabularInline` を使うと、 リレーション相手のオブジェクトはよりコンパクトなテーブル形式で表示されます:

![Add question page now has more compact choices](intro/_images/admin11t.png)

追加の "Delete?" カラムがあることに注意してください。 "Added Another Choice" ボタンで追加された行とすでに保存された行を削除するためにあります。

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

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

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

![Polls change list page](intro/_images/admin04t.png)

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

*polls/admin.py*

```
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date')
```

おまけとして、[チュートリアル その2](/ja/1.10/intro/tutorial02/) で定義したカスタムメソッド `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](intro/_images/admin12t.png)

カラムのヘッダをクリックすると、カラムの値に応じてエントリを並べ換えできます。ただし `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`](/ja/1.10/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) を参照してください。

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

```
list_filter = ['pub_date']
```

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

![Polls change list page, updated](intro/_images/admin13t.png)

フィルタの種類は、フィルタ対象のフィールドの種類に応じて変化します。`pub_date` は [`DateTimeField`](/ja/1.10/ref/models/fields/#django.db.models.DateTimeField) なので、Django はこのフィールドにふさわしいフィルタオプションが、「すべての期間 ("Any date")」「今日 ("Today")」「今週 ("Past 7 days")」「今月 ("This month")」であることを知っているのです。

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

```
search_fields = ['question_text']
```

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

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

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

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

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

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

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

設定ファイル (`mysite/settings.py`, remember) を開いて、[`TEMPLATES`](/ja/1.10/ref/settings/#std-setting-TEMPLATES) 設定オプションの中に、次のように [`DIRS`](/ja/1.10/ref/settings/#std-setting-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`](/ja/1.10/ref/settings/#std-setting-TEMPLATES-DIRS) は、Django がテンプレートを読み込む時にチェックする、ファイルシステム上のディレクトリのリストです。サーチパスのようなものです。

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

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

> **Django のソースファイルの場所はどこ？**
>
> Django のソースファイルがシステム中のどこにあるのか分からない場合は、以下のコマンドを実行してください。
>
> ```console
> $ python -c "import django; print(django.__path__)"
> ```

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

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

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

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

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

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

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

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

Django のテンプレートの検索方法に関する詳しい情報は、[テンプレート読み込み ドキュメント](/ja/1.10/topics/templates/#template-loading) を参照してください。

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

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

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

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

## 次は何をしましょうか？

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

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