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

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

## はじめに

[Django公式チュートリアル](https://docs.djangoproject.com/ja/4.0/intro/tutorial01/)、その4-2です。
前回は、アプリにフォームを追加しました。
今回は、汎用ビューについてです。

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

### 今回の内容

- 汎用ビューを使用

### 汎用ビューを使う

- 汎用ビューとは
    - よくあるパターンを抽象化し、Pythonコードすら書かずアプリを書き上げられる
        - コードが少ないのはいいことだけど、どうやって実現する?
- 投票アプリを汎用ビューシステムに変換し、 コードをばっさり捨てる
    - 変換は数ステップ
        - `URLconf`を変換
        - 古い不要なビューを削除
        - 新しいビューにDjango汎用ビューを設定
- コードを入れ換える
    - Djangoアプリを書く場合、最初から自分の問題解決に適している汎用ビューを使用
        - チュートリアルでは、説明のため途中から汎用ビューに変更

### `URLconf`を修正

- 汎用ビュー向けに`question_id`を`pk`に変更
    - 汎用ビューの場合、URLから`pk`という名前でプライマリキーをキャプチャし、汎用ビューに渡しているため
    - 2つ目と3つ目のパス文字列に該当するパターン名を`<question_id>`から`<pk>`に変更

```polls/urls.py
from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]
```

### ビューの修正

- 古い`index`、`detail`、`results`のビューを削除
- 代わりに、Djangoの汎用ビューを使用
    - `ListView`汎用ビュー
        - `オブジェクトのリストを表示する`という概念を抽象化
    - `DetailView`汎用ビュー
        - `あるタイプのオブジェクトの詳細ページを表示する`という概念を抽象化
- 汎用ビューとモデルのマッピング
    - `model`属性を使用
- 汎用ビューのテンプレート
    - デフォルトで、 汎用ビューが使用するテンプレート
        - `DetailView`汎用ビューの場合
            - `<app name>/<model name>_detail.html`
            - 例えば、`polls/question_detail.html`
        - `ListView`汎用ビューの場合、
            - `<app name>/<model name>_list.html`
    - 結果ビュー`ResultView`と詳細ビュー`DetailView`は、同じ`DetailView`汎用ビューを使用
        - `template_name`属性を指定し、異なる見た目にレンダリング可能
            - 自動生成のデフォルトテンプレート名ではなく、指定テンプレート名を使うよう Djangoに伝える
    - 索引ビュー`IndexView`は、`ListView`汎用ビューを使用
        - `template_name`属性を指定し、`polls/index.html`テンプレートを使用するように指示
- テンプレートに渡すコンテキスト変数
    - `DetailView`に、`question`というコンテキスト変数が自動的に渡される
        - 理由は、モデル`Question`とマッピングされているため、Djangoがコンテキスト変数にふさわしい名前を決めてくれる
    - `ListView`に、`question_list`というコンテキスト変数が自動的に渡される
        - 上書きするには、`context_object_name`属性を使用
        - 代替アプローチとして、テンプレート名をデフォルトのコンテキスト変数名と一致させることもできる
            - ただし、使用したいコンテキスト変数名をDjangoに伝えたほうが簡単

```polls/views.py
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

from .models import Choice, Question


class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'


def vote(request, question_id):
    ... # same as above, no changes needed.
```

### サーバーを実行し、汎用ビューベースに変更した投票アプリを検証

- VS Codeターミナルで、仮想環境をアクティベート
```
C:\kanban\pollsite>..\venv\.venv\Scripts\activate
```

- 開発サーバーを起動
```
(venv) C:\kanban\pollsite>python manage.py runserver
```

- - ブラウザで`http://127.0.0.1:8000/polls/`にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/656136e8-cfc6-7cc5-909c-ed7ed8ccdf1e.png)

質問をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/8e17184f-f2cc-9601-5cdd-e6d7834a87bf.png)

投票してみます。

## おわりに

Djangoアプリを汎用ビューに刷新しました。
次回も続きます。お楽しみに。

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