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

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

> **困ったときは:**
>
> このチュートリアルの実行に問題がある場合は、FAQ の [Getting Help](/ja/4.2/faq/help/) セクションに進んでください。

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

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

ためしに、編集フォームでのフィールドの並び順を並べ替えてみましょう。 `admin.site.register(Question)` の行を以下のように置き換えてみてください。

*`polls/admin.py`*

```python
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」フィールドよりも前に変更しています:

![フィールドの並び順が変更されました](intro/_images/admin07.png)

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

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

*`polls/admin.py`*

```python
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/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets) の各タプルの先頭の要素はフィールドセットのタイトルです。 フォームは以下のように表示されます。

![フォームにフィールドセットが追加されました](intro/_images/admin08t.png)

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

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

現状はそうですが、表示はできます。

この問題を解決するには2つの方法があります。1つ目は、 `Question` と同じようにadminに `Choice` を登録する方法です:

*`polls/admin.py`*

```python
from django.contrib import admin

from .models import Choice, Question

# ...
admin.site.register(Choice)
```

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

![adminページを選択](intro/_images/admin09.png)

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

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

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

`Choice` モデルに対する `register()` を削除して、 `Question` の登録する部分を以下のように書き換えてください。

*`polls/admin.py`*

```python
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)」ページを読み込んで、どのように見えるか確かめてみましょう。

![質問追加ページで選択肢が表示されるようになりました](intro/_images/admin10t.png)

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

現在の3つのスロットの最後に「別の選択肢を追加」というリンクがあります。 これをクリックすると新しいスロットが追加されます。追加されたスロットを削除したい場合は、追加されたスロットの右上にある×をクリックしてください。この画像は追加されたスロットを示しています:

![動的に追加されるスロット](intro/_images/admin14t.png)

しかし、一つだけ小さな問題があります。関連する `Choice` オブジェクトを入力するためのすべてのフィールドを表示するには、多くの画面スペースを必要とします。そのため、Django は関連するオブジェクトをインラインで表示する表形式の方法を提供しています。それを使うには、`ChoiceInline` 宣言を読み替える:

*`polls/admin.py`*

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

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

![質問追加ページで、よりコンパクトな選択肢を追加しました](intro/_images/admin11t.png)

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

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

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

現時点では以下のように表示されています。

![投票の変更一覧ページ](intro/_images/admin04t.png)

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

*`polls/admin.py`*

```python
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ["question_text", "pub_date"]
```

念のため、 Tutorial 2 の was\_published\_recently() メソッドも含めましょう。

*`polls/admin.py`*

```python
class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ["question_text", "pub_date", "was_published_recently"]
```

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

![更新された Polls の変更一覧ページ](intro/_images/admin12t.png)

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

これを改善するには、[`display()`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.display) デコレータを次のように (`polls/models.py` の) メソッドに使います:

*`polls/models.py`*

```python
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`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) を参照してください。

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

```
list_filter = ["pub_date"]
```

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

![更新された Polls の変更一覧ページ](intro/_images/admin13t.png)

フィルタの種類は、フィルタ対象のフィールドの種類に応じて変化します。`pub_date` は [`DateTimeField`](/ja/4.2/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/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_per_page)、[`検索ボックス`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields)、[`フィルタ`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter)、[`日付による階層化`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.date_hierarchy)、[`カラムヘッダを使った並び替え`](/ja/4.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) の機能は、すべて協調して思いのままに動作します。

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

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

ただし、Djangoのテンプレートシステムを使用して変更できます。 Django admin はDjango自体を利用しており、そのインターフェースはDjango独自のテンプレートシステムを使用しています。

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

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

設定ファイル (`mysite/settings.py`) を開いて、[`TEMPLATES`](/ja/4.2/ref/settings/#std-setting-TEMPLATES) 設定オプションの中に、次のように [`DIRS`](/ja/4.2/ref/settings/#std-setting-TEMPLATES-DIRS) オプションを追加します。

*`mysite/settings.py`*

```python
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`](/ja/4.2/ref/settings/#std-setting-TEMPLATES-DIRS) は、Django がテンプレートを読み込む時にチェックする、ファイルシステム上のディレクトリのリストです。サーチパスのようなものです。

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

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

> **Django のソースファイルの場所はどこ？**
>
> Django のソースファイルがシステム中のどこにあるのか分からない場合は、以下のコマンドを実行してください。
>
> ```console
> $ python -c "import django; print(django.__path__)"
> ```
>
> *Windows*
>
> ```doscon
> ...\> py -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/4.2/ref/contrib/admin/#django.contrib.admin.AdminSite.site_header) 属性を使えば、もっと簡単に設定できます。

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

Django のデフォルトのadminテンプレートはすべてオーバーライドできることに注意してください。テンプレートを上書きするには、`base_site.html` で行ったのと同じことをしてください -- デフォルトのディレクトリからカスタムディレクトリにコピーして変更してください。

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

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

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

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

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

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

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

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

管理画面に満足したら、 [このチュートリアルのパート8](/ja/4.2/intro/tutorial08/) を読み、サードパーティ製パッケージの使い方を学んでください。
