Django+Reactで学ぶプログラミング基礎(15): Djangoチュートリアル(投票アプリその7-1)

##### [前回] [Django+Reactで学ぶプログラミング基礎(14): Djangoチュートリアル(投票アプリその6)](https://qiita.com/mingchun_zhao/items/1ce0b06df314694b3e1a)

## はじめに

[Django公式チュートリアル](https://docs.djangoproject.com/ja/4.0/intro/tutorial01/)、その7-1です。
前回は、Djangoの静的ファイルをカスタマイズしました。
今回は、管理サイトをカスタマイズします。

## Djangoアプリ作成(その7-1): 投票(poll)アプリ

### 今回の内容

- 管理フォームをカスタマイズ
- リレーションを張ったオブジェクトを追加

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

- デフォルトで、モデル登録フォームの作成方法
    - `admin.site.register(モデル名)`を呼び出す
        - すると、Djangoがデフォルト形式でオブジェクトを表示してくれる
- 管理フォームの表示や操作をカスタマイズしたい
    - オブジェクト登録時に、オプションを指定
        - モデルごとに`admin`クラスを作成し、`admin.site.register()`の2番目の引数に渡す

#### 質問登録フォームでフィールドの並び順を変えてみる

- `Publication date`フィールドの表示位置を`Question`フィールドより前に変更
```polls/admin.py
from random import choices
from django.contrib import admin

from .models import Question,Choice

class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

admin.site.register(Choice)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/b269ce5e-90a3-4275-a664-4ab3101eedcf.png)

- VS Codeからサーバーを起動(すでに起動済みの場合は再起動)
```
C:\kanban\pollsite>..\venv\.venv\Scripts\activate
(venv) C:\kanban\pollsite>python manage.py runserver
```

- ブラウザで、管理サイトの質問登録画面にアクセス
    - `http://127.0.0.1:8000/admin/polls/question/add/`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/e3d7b510-8fa2-ba48-91c4-b8e416cee390.png)

#### フォームを複数のフィールドセットに分割してみる

- `fieldsets`の各タプルの先頭の要素が、フィールドセットのタイトル
```polls/admin.py
from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('質問文',   {'fields': ['question_text']}),
        ('日付情報', {'fields': ['pub_date']}),
    ]

admin.site.register(Question, QuestionAdmin)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/77572a59-48c3-922e-41bf-d7fba4f53c10.png)

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

#### 質問の管理ページに、関連する選択肢を表示したい

##### 方法1: 質問と同じ方法で、管理サイトに`Choice`を登録(実装済)
```polls/admin.py
from random import choices
from django.contrib import admin

from .models import Question,Choice

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        ('質問文',   {'fields': ['question_text']}),
        ('日付情報', {'fields': ['pub_date']}),
    ]

admin.site.register(Question, QuestionAdmin)

admin.site.register(Choice)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/35fd5312-e752-968a-ed9a-3c38f47112a2.png)

- ブラウザで、管理サイトの選択肢登録画面にアクセス
    - `http://127.0.0.1:8000/admin/polls/choice/add/`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/c9f6acc2-0f36-5037-3065-f3ec353660be.png)

- 選択肢登録フォームで、`Question`フィールドは選択ボックスとなる
    - データベース上の全ての質問を選択可能
    - Djangoは外部キー(`ForeignKey`)表示に、`<select>`ボックスを使ってくれる
- `Question`フィールドの隣にある`+(質問を追加）`リンクをクリック
    - `ForeignKey`オブジェクトに対し、デフォルトでこのリンクが表示される
    - `質問を追加`ポップアップウィンドウが表示される
        - 質問を追加し、`保存`を押すと、質問がデータベースに保存される
        - 元の、`選択肢追加`フォームに選択済み項目として動的に追加される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/e2d388e8-f2e0-bbba-7c49-1ba28f51f731.png)

##### 方法2: 質問追加時、選択肢も合わせて追加

- 方法1の問題
    - `Choice`オブジェクトのシステムへの追加が非効率
    - 解決案
        - `Question`オブジェクト追加時に、`Choice`をひと揃い追加する
- `Choice`モデルに対する`register()`を削除し、
    - `Question`登録部分を書き換え、以下のように指示
        - `Choice`オブジェクトは`Question`管理ページから編集
        - デフォルトで、 3つの`Choice`フィールドを用意
```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)
```

- ブラウザで、質問登録ページを更新(F5)
    - `http://127.0.0.1:8000/admin/polls/question/add/`
    - `Question`と関連付けられた`Choice`登録用に、3つのスロットが存在(`extra`に指定した数)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/4fd997ce-f69b-a941-0f0a-611a0d9685bd.png)

- リレーション先のオブジェクトをコンパクトなテーブル形式で表示
    - `ChoiceInline`宣言を変更
        - `StackedInline`を`TabularInline`に書き換える
```polls/admin.py
class ChoiceInline(admin.TabularInline):
    #...
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/2fb1037b-e175-346d-6557-d6e3fb49f98a.png)

## おわりに

管理サイトをカスタマイズしました。
次回も続きます。お楽しみに。

##### [次回] [Django+Reactで学ぶプログラミング基礎(16): Djangoチュートリアル(投票アプリその7-2)](https://qiita.com/mingchun_zhao/items/ac48f5064ff466a0e0c0)