Djangoとビュー画面、データベースを紐付ける(初学者がどこまで理解できるか(4))

#Djangoで出金記録を投稿・表示してみます
[Djangoでデータベース構築 (初学者がどこまで理解できるか(３))](https://qiita.com/nn24nHi/items/bd23d736226547f80f8f) の続きです。
今回は、お金を使った記録ができ、それが表示されるようにします。

<やりたいこと>
・いつ、何の用途に、何円使ったかを投稿 (データベースに保存)
・用途毎に記録を表示 (HTMLのテーブル表示)

<次回以降にやりたいこと>
・レイアウトの整理
・ソートなど機能を持たせること
->CSSやJavaScriptの実装かな??

## データベースの整理
```py
#### practice/myapplication/models.py

from django.db import models

class Payment(models.Model):
    PAYMENT_CHOICES = (
        ('Food', '食費'),
        ('Clothes', '衣類'),
        ('Other', 'その他'),
    )
    payment_date = models.DateField(blank=True)
    payment_type = models.CharField(max_length=50, choices=PAYMENT_CHOICES, blank=True)
    payment_name = models.CharField(max_length=50, blank=True)
    payment_price = models.IntegerField(blank=True)

    # 管理画面(admin)で、文字列表示したいためで、特になくても良い。
    def __str__(self):
        return self.payment_name
```
データベースへのテーブルとして用意したのは、１種類`Payment`だけです。
その中に、日付：`payment_date`、項目：`payment_type`、品目：`payment_name`、金額：`payment_price`の列（Field）を用意しました。

`blank=True`：入力漏れがあるとエラーにする
`choices`：選択形式にする

これで、データベースとしては「いつ、どの項目で、何を、いくらで」買ったか記録するテーブルの形は完了です。

## ユーザーからデータベースへ書き込むフォームを用意
```py
#### practice/myapplication/forms.py
# 新しくファイルを作る必要があります

from django import forms
from .models import Payment


class PostForm(forms.ModelForm):
# ModelFormクラスを継承。データベースに保存するには、Metaクラスが必要
    class Meta:
# models.pyの使用したいクラス(Model)と、Fieldを記載
        model = Payment
        fields = ('payment_date', 'payment_type', 'payment_name', 'payment_price')
```
`forms.py`は自動で生成されないため、ファイルを作成する必要があります。
`Meta`クラスに関しては、調べてみても今のところ、データベースに保存するために必要なものとしか理解できておらず、ここについては追々認識を深めます。
確かに、Metaクラスなしで、動作はしませんでした。。。

## ビュー画面への出力内容を用意
```py
from django.shortcuts import render, redirect
from django.views.generic import FormView
from .forms import PostForm
from .models import Payment

# 必要ないですが"Hello world"表示画面です。
def index(request):
    return render(request, 'myapplication/index.html')

# 出金記録の投稿画面です
class PostView(FormView):
    template_name = 'myapplication/post.html'
    form_class = PostForm
    def form_valid(self, form_class):
        form_class.save()
        return redirect('myapplication:payment_result')

# 出金記録の一覧表示画面です。
def payment_result(request):
    food_list = Payment.objects.filter(payment_type='Food').order_by('payment_date')
    clothes_list = Payment.objects.filter(payment_type='Clothes').order_by('payment_date')
    other_list = Payment.objects.filter(payment_type='Other').order_by('payment_date')

    context = {
        'food_list' : food_list,
        'clothes_list' : clothes_list,
        'other_list' : other_list
        }
    return render(request, 'myapplication/payment_result.html', context)

```
・投稿画面について
`FormView`クラスを継承しています。
この中の`form_valid`関数を使用することで、フォームに入力して成功すれば次の処理に移る、という動作を実現しています。
ここでいう次の処理とは、`form_class.save()`(データベースへの保存)と、`payment_result.html`へのリダイレクトです。

・一覧画面について
出力は、`models.py`で分けたchoices毎（項目毎）に出力します。
`order_by('payment_date')`：日時が昇順になるよう指定しました。
`context`は、HTML分に埋め込めるよう、render()で引き渡します。

## ビュー画面(HTML)の設定　（投稿画面）
```html
<!-- practice/myapplication/templates/myapplication/post.html -->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <h2>入力画面</h2>
    <form action="" method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit" value="送信">Go</button>
   </form>
  </body>
</html>
```

`{% csrf_token %}`：脆弱性対策のために記載。
`{{ form.as_p }}`：先に設定したformを、\<p>形式で表示。
これでひとまず、「いつ、どの項目で、何を、いくらで」買ったかを、送信するページはできました。
<img width="272" alt="スクリーンショット 2019-10-14 23.33.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498092/13a1bba3-1c41-b07c-130f-754e57014725.png">
## ビュー画面(HTML)の設定　（一覧画面）
```html
<!-- myapplication/templates/myapplication/payment_result.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <h3>食費</h3>
    <table>
      <tr>
        <th>日付</th>
        <th>項目</th>
        <th>購入品</th>
        <th>金額(円)</th>
      </tr>
        {% if food_list %}
          {% for f in food_list %}
          <tr>
            <td>{{ f.payment_date }}</td>
            <td>{{ f.payment_type }}</td>
            <td>{{ f.payment_name }}</td>
            <td>{{ f.payment_price }}</td>
          </tr>
          {% endfor %}
        {% endif %}
    </table>
    ----------------------------------------
    <h3>衣類</h3>
    <table>
      <tr>
        <th>日付</th>
        <th>項目</th>
        <th>購入品</th>
        <th>金額(円)</th>
      </tr>
        {% if clothes_list %}
          {% for c in clothes_list %}
          <tr>
            <td>{{ c.payment_date }}</td>
            <td>{{ c.payment_type }}</td>
            <td>{{ c.payment_name }}</td>
            <td>{{ c.payment_price }}</td>
          </tr>
          {% endfor %}
        {% endif %}
    </table>
    ----------------------------------------
    <h3>その他</h3>
    <table>
      <tr>
        <th>日付</th>
        <th>項目</th>
        <th>購入品</th>
        <th>金額(円)</th>
      </tr>
        {% if other_list %}
          {% for f in other_list %}
          <tr>
            <td>{{ f.payment_date }}</td>
            <td>{{ f.payment_type }}</td>
            <td>{{ f.payment_name }}</td>
            <td>{{ f.payment_price }}</td>
          </tr>
          {% endfor %}
        {% endif %}
      </table>
      ----------------------------------------
  </body>
</html>
```
簡単にまとめると、各***_listが`views.py`にあるなら、\<td>内にデータを返すという内容です。
<img width="508" alt="スクリーンショット 2019-10-14 23.37.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498092/47787fe2-8100-5e8a-5e1e-ca89a944f3a6.png">

## URLを紐付けます。admin設定も更新します
```py
#### practice/myapplication/urls.py

from django.urls import path
from . import views

app_name = 'myapplication'

urlpatterns = [
    path('', views.index, name='index'),
    # クラス(PostView)を使用した、post/へのpathのみ、as_view関数を使用しています。
    path('post/', views.PostView.as_view(), name='post'),
    path('payment_result/', views.payment_result, name='payment_result'),
]
```

```py
#### practice/myapplication/admin.py

from django.contrib import admin
from .models import Payment

admin.site.register(Payment)
```
これで準備ができました。最後にデータベースの更新と、開発サーバーを起動します。
ブラウザで、post/と、payment_result/にアクセスすると、先のHTMLが表示されます。

```py
#### practice/
$ python manage.py makemigrations myapplication
$ python manage.py migrate
$ python manage.py runserver
```

## さいごに
データベースへの入力・更新・出力、どうするのか最初全くわかりませんでしたが、forms.py、大変便利な機能だと思いました。
次は体裁を整えていきたいと思います。
CSSやJavascriptを使うかなと思っていますが、反映の仕方とかDjangoルールがあるのかな？
また、整理できたら更新します。