TemplateViewをつかってみよう！Detailview編

#今回使用するのはDetailView

目標はListViewで表示されるデータの詳細を見れるようにすることです。
[前回のIndexView作成チュートリアル](https://qiita.com/flee-style/items/ab99a3d6b568c4b674b5)の続きの内容になります。

超絶シンプルに書いており、djangoに慣れようという目的が強いため、手を動かしてDetailViewに慣れたいといった方向けに記事を書いています。

また、初めからTemplateViewを練習したい方は初めの[IndexView作成チュートリアル](https://qiita.com/flee-style/items/ab99a3d6b568c4b674b5)からご覧になると、確実に作成ができます。

##ファイル構成

```
mysite
│  db.sqlite3
│  manage.py
├─mysite
│  │  settings.py
│  │  urls.py
└─test_app
    │  admin.py
    │  apps.py
    │  models.py
    │  tests.py
    │  urls.py(ここをルーティング設定のために編集)
    │  views.py(ここをDetailView設定のために編集)
    ├─migrations
    └─templates
    　  └─test_app
    　       └─list.html(ここを詳細ページに飛ぶために編集)
               detail.html(ここをページ表示のために編集)
```

##前回作成したShopモデルを使用します。
使用するモデル名は`Shop`です。
nameは３０文字制限の文字列フィールド、
tell_numは１３文字制限の文字列フィールド、
addressは３０文字制限の文字列フィールド、
created\_atは作成日と時間が初期値に設定される日時フィールドを定義しています。

```models.py
from django.db import models

# Create your models here.
class Shop(models.Model):
    #各フィールドの定義
    name = models.CharField('shopname',max_length=30)
    tell_num = models.CharField('tell_number',max_length=13)
    address = models.CharField('address',max_length=30)
    created_at = models.DateTimeField(auto_now_add=True)

    #admin画面の表示内容
    def __str__(self):
        return self.name
```

##数字で詳細を表示するためにルーティングを設定する
ここでは`http://local:8000/1/`など、数字を入れるだけで紐づく詳細ページにアクセスできるように設定します。
このような`path`を書いてあげます。

```urls.py
from django.urls import path
from . import views

app_name = 'test_app'
urlpatterns = [
    path('index/',views.IndexView.as_view(),name='index'),
    path('<int:pk>/',views.DetailView.as_view(),name='detail'),
]
```
int型のpkという数字が入るところを用意します。`views.py`の`DetailView`を`as_view()`でビューとして設定しています。パスの名前は`detail`とし、htmlファイルから`url`を指定する場合は`detail`と指定できるようにします。わかりやすい`name`にしてあげると使いやすいですね。

##views.pyでDetailViewを設定

```views.py
from django.views import generic
from .models import Shop

class IndexView(generic.ListView):
    model = Shop
    template_name = 'test_app/list.html'

#ここを追加
class DetailView(generic.DetailView):
    model=Shop
    template_name = 'test_app/detail.view'
```
djangoで用意されているgenericライブラリの中のDetailViewを継承しています。
モデルとテンプレートの指定のみで設定完了です。

##detail.htmlで詳細ページを作成
templates/test_app/detail.htmlを新たに作成します。
ファイル名はビューファイルで`template_name`として指定されています。

```detail.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  <p>{{ shop.name }}の詳細</p>
  <table>
    <tr>
      <th>店名</th>
      <th>電話番号</th>
      <th>住所</th>
      <th>ページ作成日</th>
    </tr>

    <tr>
      <td>{{ shop.name }}</td>
      <td>{{ shop.tell_num  }}</td>
      <td>{{ shop.address  }}</td>
      <td>{{ shop.created_at }}</td>
    </tr>
  </table>
  </body>
</html>
```
shop.といった形式でモデルで定義した項目を{{ }}でくくり指定すると、html側で表示がなされます。
DetailViewを用いるとモデル名にそって先頭が小文字となったものを指定できます。
自動でこれまた設定してくれているので、モデルデータの指定が楽です！
例えば、モデル名を`Shop`とすると、`shop.`と指定することになります。Carだとcarですね！

##リスト表示のindexページに詳細リンクを張ります。
このファイルでShopモデルのデータを表形式で表示しています。

```list.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
  <p>一覧の表示</p>
  <table>
    <tr>
    </tr>

    {% for shop in shop_list %}
    <tr>
      <td>{{ shop.name }}</td>
      <td><a href="{% url 'test_app:detail' shop.pk %}">詳細</a></td>
    </tr>
    {% endfor %}
  </table>
  </body>
</html>
```

```
<a href="{% url 'test_app:detail' shop.pk %}">詳細</a>
```
が難しいのですが、htmlの`a`グで、リンク先をurls.pyの`app_name`で指定した`test_app`の`name`が`detail`である`path`と、`shop`の`pk`とあわせて記述されています。
ここではshop.idとしてもかまいません。

これで、詳細をクリックできるリンクが作成できました。
##ページの動作確認
サーバーを起動し

```
$python manage.py runserver
```
indexページを指定すると

```
http://localhost:8000/index
```
![detail_list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547305/22f75847-b9e3-c161-7e51-664da04f6cf0.png)

そこで、詳細をクリックすると。

```
http://localhost:8000/1
```
![detail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547305/d643dde6-4472-aafb-d305-0c1bb0682ec2.png)

完成です！

##うまく動かない方へ「error:Generic detail view DetailView must be called with either an object pk or a slug in the URLconf.」
`Generic detail view DetailView must be called with either an object pk or a slug in the URLconf.`が出てしまう場合、pkをurls.pyに用いないといけませんので、注意しましょう。`<int:pk>`と`pk`以外受け付けません。
ビューを呼び出す場合、pkという変数名の決まりでモデル指定中のShopモデルから該当オブジェクトを呼び出すのです。例えば、タピオカ屋をクリックすると、データのprimary keyが１のデータの詳細がdetai.htmlのルールに沿って表示がなされます。


#最後に
このように、テンプレートビューであるDetailビューなどを用いる場合、ルールがとても多いため混乱する恐れが高いです。💦
指定されたように書かないと動かないことが多いため、サンプルを見てたくさん書いてみると、書き方に慣れてきます。
やはり、自分で好みのサイトを全く同じでもいいので作ってみると勉強になりますよ！