【Python Django】初心者プログラマーのWebアプリ#5 【データベースの値扱う】

第五回目Python Djangoフレームワークでデータベース(SQL)を使ったデータの取扱と実装というテーマで初心者プログラマー用のwebアプリ作成チュートリアル始めます。

前回はformを扱いました。

https://qiita.com/Bashi50/items/93da7a071b2e02dc21ed

データベースからどんな感じで取得できるかテストするなら以下も参考にしてみてください。

[:heavy_check_mark: Django Query データベースから取得](https://qiita.com/Bashi50/items/9e1d62c4159f065b662b)

リレーション関係：
[:heavy_check_mark: [初心者] #2 Django Query データベース取得 1対多と多対多](https://qiita.com/Bashi50/items/2cdff211c45e54a4c17c)

A.I、画像認識機械学習、自然言語系の機械学習だろうがデータを作成して保存するのはデータベースです。
データベースに保存された値を使って綺麗でわかりやすいページを作ることも、きれいなグラフが表示できるWebアプリも作れます。

![er.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/fc64dcf5-32bf-7ceb-7105-c8398817d2f8.png)


# :pushpin: Pythonで作るDjangoアプリ記事一覧
|  | 内容|
|:----:|:----:|
|**part1**|[簡単な文字表示アプリを作る](https://qiita.com/Bashi50/items/c686d30fa4fdba4582ad)|
|**part2**|[HTMLテンプレート表示](https://qiita.com/Bashi50/items/b9666030a058795c7dd8)|
|**part3**|["画像" "CSS" "Javascript"実装](https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392)|
|**part4**|[フォーム送信](https://qiita.com/Bashi50/items/93da7a071b2e02dc21ed)|
|**part5**|**<font color="red">データベースの値取得・更新</font>　← ココ**|
|**番外編**|[Django AWS デプロイ](https://qiita.com/Bashi50/items/d5bc47eeb9668304aaa2)|
|**APIサーバー編**|[Django REST frameworkとReact #4](https://qiita.com/Bashi50/items/203505440049d759b694)|
|**単体テスト編**|[Django unittestで単体テスト](https://qiita.com/Bashi50/items/4205c398f475bc69f631)|

## ソースコード
この章のコードは以下です。確認やコピペでどうぞ

https://github.com/kohishibashi/easy-django/tree/part5

# 今回の流れ

1. モデルクラス作成
2. マイグレーションファイルとmigrate
3. 管理ページ作成
4. 管理ページよりデータベースに情報追加(レコード追加)
5. DB追加データを表示する。取り扱う。
6. DBの基本CRUD (作成 取得 更新 削除)できるページを作る

# 1. モデルクラス作成
Djangoは`Django ORM`というORマッパーを使えます。
初心者向けなので簡単に説明にとどめますが、ORマッパー使うと

- データベースを操作するためのSQLでなくて、PythonのコードでSQLをいじることができる
- データベースの構成情報をDjangoで管理できる。変更や修正などわかりやすいし変更履歴も追える
- ある程度最適化してSQL書ける
- データベースには種類があって、MySQL, PostgreSQL, SQLiteなどありますが、書き方の方言みたいなものの違いを気にしなくても使える

などの利点あるので最近の開発では広く使用します。

:::note info
以下、データベースのことをDBと記述します。ついでに今回使用するのはデフォルトで使えるDBのSQLiteを使用します。DjangoならDBの設定が必要なく使用できて便利です。
:::

## 1-1. テーブルを作る
データを保存するためにテーブルを作成する必要があります。ORマッパー使わないとSQLで定義を書いて作成することになりますが、Django ORマッパーならPythonのclassにテーブルの定義を記述すればいい感じで作ってくれます。

データを保存する手順として

1. `models.py`にテーブルのデータ構造などを記述
2. マイグレーションファイルを作成（コマンドで作成）
3. migrateコマンドでDBに反映させる

という流れでテーブルが作れます。

:::note info
複数のテーブルを格納する「DB」は作成する必要ありません。次に使用するmigrateをすることでDBやDjangoでデフォルトで使う機能を使うためのテーブルを作成してくれます。
:::


## 1-2. models.py作成
では、さっそくデータを保存したいのでデータベースにテーブルを作っていきましょう。 
アプリケーションのフォルダにある`models.py`に以下のように書くきます。
以下に示すのがデータ構造です。例としていろんな社員情報登録アプリでも作ってみます。

```testproject/webtestapp/models.py
from django.db import models
from django.utils import timezone

class Employee(models.Model):
    name = models.CharField('名前', max_length=255)
    mail = models.EmailField('メール', max_length=255)
    gender = models.BooleanField('性別')
    department = models.CharField('部署', max_length=255)
    year = models.IntegerField('社歴', default=0)
    created_at = models.DateField('作成日', default=timezone.now)

    def __str__(self):
        return '<id:' + str(self.id) + ',' + self.name + ',' + self.department + '>'
```

modelクラスを継承して作成しています。前回記事の`forms.py`と似ていて、便利に使える機能がこの`models.Model`に入っているので、あとはデータを定義していく感じです。

:::note info
「性別」って項目わかりにくいですが、チェックしたら女性と定義します。簡単なBooleanFieldの例も書いたほうがいいかな…との考えなのでお許しください。
:::

### データの定義
書いたコードの説明をします。取り合えず動かしたいだけなら[2. マイグレーションファイルとmigrate](#2-マイグレーションファイルとmigrate)に行っても大丈夫です。

以下がテーブルに登録するデータの型を定義しています。

- `models.CharField()`: 文字列のデータ保存
- `models.EmailField()`: 文字列での保存だが、メールアドレスとして保存するときに便利な機能あり
- `models.BooleanField()`: TrueかFalseのデータ保存
- `models.IntegerField()`: 整数型のデータ保存
- `models.DateField()`: 日付データの保存。時間も保存したいなら`models.DateTimeField()`使用する


### オプションで定義をより細かく

あとはオプションですね。オプションの指定は任意です。
ただ、オプションを指定することで、テーブルに入るデータの制約やデフォルト値など指定することができます。

- 第一引数に日本語で入力してます。表示上の名前です。今回は日本語にしました。
- `max_length=255`は最大文字数。
- `year = models.IntegerField('社歴', default=0)`デフォルト値を指定。
  - `year`を新規登録時に指定しなかったら`0`を入れるという意味。
- `models.DateField('作成日',default=timezone.now)`は今日の日付`timezone.now`をデフォルト。
  - 以前`settings.py`でタイムゾーンを日本にしたので日本時間で新規登録した瞬間の時間が登録されることになる

最後に、`__str__`ですが、これも任意です。別に書かなくても問題にはなりません。
これは実際に見てみないと今いちわからないと思いますが、DBから取得したデータが入っていることになる`Employee`モデルを表示したときにどんな表示にするか？というのを指定してます。

:::note info
models.DateField()には便利なオプションもあります。
<font color="red">auto_now_add</font>：新規作成時の日にち自動登録
<font color="red">auto_now</font>：新規作成・更新日にち自動登録
:::


# 2. マイグレーションファイルとmigrate
`models.py`にテーブルの定義を書いたのでこの章では、DBにテーブルを作成します。

## 2-1. データベースへの移行「migrations」
DBを作成したとき、定義を更新するとき、削除するときに使うのがmigrationsコマンドです。
コマンドを実行することでマイグレーションファイルを作成します。

`manage.py`というファイルがあるディレクトリでターミナルに入力

```
python manage.py makemigrations webtestapp
```

最後の`webtestapp`はアプリケーション名です。
`python manage.py <アプリケーション名>`

```
Migrations for 'webtestapp':
  webtestapp\migrations\0001_initial.py
    - Create model Employee
```
上記みたいな表示でエラーがなければ`0001_initial.py`というマイグレーションファイルが作成されます。
そうしたらマイグレーションファイルの内容を適用してテーブルを作成しましょう

```
python manage.py migrate
```
以下のような表示がされるはず。

```
.
.
.
  Applying webtestapp.0001_initial... OK
```

これでDBに`models.py`に定義したテーブルが作成されます。

:::note info
一度もmigrateを実行したことがない場合はDjangoで作るアプリを使うのに必要なデータを保存するためのテーブルを何個も作られます。とりあえず作られるものは気にしなくても問題ないです。
:::


# 3. 管理画面を使えるようにする
Djangoのとても良いところの一つとして管理画面をデフォルトで使えることがあげられます！
データベースのデータを画面でポチポチするだけで登録したり、更新したり、削除することができてとても便利です。

テストデータを登録するのにもとても便利なので使ってみましょう。


## 3-1. 管理者ユーザー追加
まずは管理画面にログインしないといけないので管理者ユーザーを作成します。
以下のコマンド打って、`Username` `Email` `Password`登録すると管理者ページを自動で作成してくれます。

```
python manage.py createsuperuser
```

Email Passwordなどの情報は変更もできますし、練習ならばテキトウでいいです。
（テストなら`admin` `test@sample.com` `passwaord`とか）

:::note warn
パスワードが脆弱とか出るかもしれませんが、ローカルなら問題ないです。
:::

> http://127.0.0.1:8000/admin

でアクセスすると

![キャプチャ.JPG](https://qiita-image-store.s3.amazonaws.com/0/260345/5460fbfc-d67c-e472-99c6-6ecfd25e9e15.jpeg)

こんな画面でますので先ほど入力したユーザー名とパスワード入力して入ります。

:::note warn
settings.pyでLANGUAGE_CODE = 'ja'にしてないと日本語になりませんので注意。
:::


![キャプチャ.JPG](https://qiita-image-store.s3.amazonaws.com/0/260345/5afdb4fb-ed7f-7ac7-bee3-4b5673178886.jpeg)

開いたはいいんですが、この状態だとまだEmployeeのデータをいじれず、デフォルトでいじれる基本情報の編集や追加しかできません。
なので作成した`Employee`を追加してみます。追加するのは`admin.py`です

```testproject/webtestapp/admin.py
from django.contrib import admin
from .models import Employee

admin.site.register(Employee)
```
追加したので再度リロードしてみると表示されると思います。

![adminpage.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/e0a0b005-0783-e969-4432-013d0372aa58.png)


Employeesというのが追加されました！次の章からテストデータの追加していきます。

# 4. 管理画面よりデータベースに情報追加(レコード追加)

追加を押していくと下のような画面になり、情報を入力するとEmployeeテーブルのレコードが登録されます。

便利ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/6f86cff1-f78c-42f7-79e0-d8af2e433a4a.png)


※「性別」はチェックしたら女性と定義します。


何件か情報追加しました。佐藤、鈴木、高橋、田中、伊藤さんです。名字ランキングトップ5位らしいですよ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/bd9ecf76-f429-4886-84e4-10acd75c93ab.png)

:::note info
Employeeの def__str__() に記述したのでこのように表示されます。試しに__str__を消してみるとよくわからない表示になると思います。よければ試してみてください。
:::

# 5. DB追加データを表示できるWEBページを作る
先ほどまで表示されていたのは管理画面で、管理者としてログインしてないと見れないのでは用途が限られますよね。
ということで、追加した情報をページに表示させてみます。

> http://127.0.0.1:8000/info

にアクセスしたら情報が表示されるページ作ります。
そのためにURLとviewの処理を紐づけます。そのために`urls.py`に追記します。

```testproject/webtestapp/urls.py
from django.urls import path
from . import views

app_name='webtestapp'

urlpatterns = [
    path('', views.index, name='index'),
    path('info', views.info, name='info'),    
]
```

`http://127.0.0.1:8000/info`にアクセスしたらviews.pyのinfoメソッドが実行されるように指定しました。
ということで、`infoメソッド`を作ってみます。


```testproject/webtestapp/views.py
from .models import Employee  # import追加
.
.
.
def info(request):
    employees = Employee.objects.all()
    employees_2 = Employee.objects.values()
    my_dict2 = {
        'title':'テスト',
        'val': employees,
        'val2': employees_2,
    }
    return render(request, 'webtestapp/info.html', my_dict2)
```

DBに登録した内容すべてを取得して、試しにどんな表示されるかテストします。
表示させるために`info.html`というテンプレートを使うように指定したのでテンプレートを作成してみましょう。


```testproject/webtestapp/templates/webtestapp/info.html
<!DOCTYPE html>
{% load static %}
<html lang="ja">
<head>
  <title>{{ title }}</title>
  <link rel="stylesheet" href="{% static "css/style.css" %}" />
</head>
<body>
  <h1>{{ title }}</h1>
  <p>{{ val }}</p>
  <table>
    <tr>
      <th>.objects.all()の表示</th>
    </tr>
    {% for i in val %}
    <tr>
      <td>{{ i }}</td>
    </tr>
    {% endfor %}
  </table>
  <p>{{ val2 }}</p>
  <table>
    <tr>
      <th>.objects.values()の表示</th>
    </tr>
    {% for i2 in val2 %}
    <tr>
      <td>{{ i2 }}</td>
    </tr>
    {% endfor %}
  </table>
</body>
</html>
```

少し見にくかったのでCSS足しました。第三回に追加したものに追記してます。

https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392

```testproject/static/css/style.css
...
table {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
}

table th,
table td {
    padding: 10px 0;
    text-align: center;
}

table tr:nth-child(even) {
    background-color: #87CEFA
}
```

見た目が雑です。すみません:innocent:

## .objects.all() .objects.values()で値取得した結果
見にくいですけど、.all()と .values()の違いみるために表示できてます。

![info_page1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/6becbb20-3708-a86c-a42e-c5eda0b24a7f.png)


どちらでも`QuerySet`が得られています。
`.all()`はすべて取得してますが、表示すると`models.py`の前に書いた`__str__`によりこのような表示になってます。
`.values()`は少し違いますね。Pythonのlistにdictとしてデータが入っている感じです。

この`.values()`は引数を指定しないとすべて表示されてこのように登録した名前、メール、性別、部署、社歴、作成日をすべて表示します。なので、表示するものを指定してやると指定したレコードのみ取得します。

`infodata2 = Employee.objects.values('id','name')`


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/8a6ed9a8-cf9c-fd3c-cf12-af6567466eb6.png)


## リストで取り出す
他にもリストみたいな形式で取り出す方法もあるので簡単に触れてみます。
values.py変更。
`employees = Employee.objects.values_list('id','name','department')`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/7a3f2072-4450-810c-bfe3-5d2803d77b1f.png)


タプルじゃん。っていうツッコミあるけどね。

一応ここまでのソースコード

https://github.com/kohishibashi/easy-django/tree/90ca0b546cf0b150b232451cf8903e88c1058b65


## DBのデータ使って表にしてみる
DjangoでDBからデータをとってきたらどんな風に使うか、もう少し試してみるために表を画面に作ってみましょう。
[ 以前 ](https://qiita.com/Bashi50/items/b9666030a058795c7dd8#%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%81%A7-for-%E3%81%AB%E3%82%88%E3%82%8A%E3%83%AA%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%95%E3%81%9B%E3%82%8B)やったようにテンプレートは`{% for %}`や`{% if %}`を使って表示を作ることができるので試してみましょう。

```testproject/webtestapp/views.py
def info(request):
    infodata = Employee.objects.all()
    header = ['ID','名前','メール','性別','部署','社歴','作成日']
    my_dict2 = {
        'title': 'テスト',
        'employees': employees,
        'header':header
    }
    return render(request, 'webtestapp/info.html',my_dict2)
```
.all()で`Employee`に登録されているすべてのデータ取り出しています。
htmlにベタ書きで表のヘッダー部分書いてもいいですが、今回はリストで渡して表示。 

```html:info.html
  <body>
    <h1>{{ title }}</h1>
    <table>
      <tr>
      {% for col in header %}
        <th>{{ col }}</th>
      {% endfor %}
      </tr>

      {% for employee in employees %}
      <tr>
        <td>{{ employee.id }}</td>
        <td>{{ employee.name }}</td>
        <td>{{ employee.mail }}</td>
        <td>{% if not employee.gender %}男{% endif %}
            {% if employee.gender %}女{% endif %}
        </td>
        <td>{{ employee.department }}</td>
        <td>{{ employee.year }}</td>
        <td>{{ employee.created_at }}</td>
      </tr>
      {% endfor %}
  </table>
  </body>
```

:::note info
初心者向けなので一応説明。Booleanなの分かってるのでTrueなら「employee.gender」で良くて、Falseなら「not employee.gender」で大丈夫です。「employee.gender == True」 「employee.gender == False」と同じと思っておいて大丈夫です。
:::

今回なら`employees`に5人分のデータが入っているので`for`で一つずつ取り出して表示させています。

テーブルの中身はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/192910be-390c-e94f-e115-11afe26c31ac.png)
`employees`からデータを取り出した`employee`にはこのデータの1行分のデータが入るのでそれで表示を作るわけです。


で作ったらこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/5e3185ec-4dc8-db74-9c2f-6dd8270db22b.png)

テキトウに入力したら女性が強そうな組織なってますけどこんな感じで表示されます。

# 6. DBの基本CRUD (作成 取得 更新 削除)できるページを作る
これまでは、管理画面で登録したデータと、登録したデータの表示だけでした。
でも、自分で作った画面からDBに登録したり、更新・削除できないとWEBアプリっぽくないですよね。

というわけで、画面からデータの作成 取得 更新 削除させる機能を作っていきましょう。

## 6-1. CRUDを作る。CRUD (Create)
まずはデータの作成から。ブラウザからデータを入力してもらってDjangoで処理するためには画面にフォームがないとユーザーは入力できませんよね。

ということでフォームを最初に作ります。[ 前回記事 ](https://qiita.com/Bashi50/items/93da7a071b2e02dc21ed)でformを作りましたが、それとは別の作り方で、今回はModelFormクラスを継承して作ることにします。

:::note info
ModelFormはmodels.pyに書いた定義を使って最小限のコードでフォーム作れる便利機能だと思っておけばOKです。単純なCRUD機能を作るときにはとても便利です！
:::

ではCRUDのC (Create)作っていきます。
### ModelFormクラスで新規登録のためのform作る
データを送るために必要なフォームを作ります。

```testproject/webtestapp/forms.py
from .models import Employee  # importに追加
...

class EmployeeAdd(forms.ModelForm):
    class Meta:
        model = Employee
        fields = ['name','mail','gender','department','year','created_at']
```

ModelFormはmodelの定義を使って定義するので、内部の`Meta`クラスにデータをいじりたいモデルを指定。そして、`field`には使用するデータ(カラム)を指定します。今回は`id`以外すべて使用することにします。

### 新規作成ページを作る
データの新規作成を行うためのフォームを表示するための画面を作成しましょう。
新しくテンプレート`create.html`を作成します。

```testproject/webtestapp/templates/webtestapp/create.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>{{ title }}</title>
  <link rel="stylesheet" href="{% static "css/style.css" %}" />
</head>
<body>
  <h1>{{ title }}</h1>
  <table>
    <form action="{% url 'webtestapp:create' %}" method="post">
      {% csrf_token %}
      {{ form.as_table }}
      <tr>
        <td></td>
        <td><input type="submit" value="送信"></td>
      </tr>
    </form>
  </table>
</body>
</html>
```
`{{ form.as_table }}`部分は`<tr>,<th>,<td>`タグに囲って作ってくれる便利機能です。まとめて入力フォームを作るときには便利ですね。

ただ、これだとまだ表示できません。`create`メソッド作ってないし、`title` や`form`というデータが必要ですので`views.py`にformを表示するための処理を追加します。

`modelform_dict`にformの表示と処理とかいい感じで作ってくれる`ModelForm`の`EmployeeAdd`を渡すようにしました。

```testproject/webtestapp/views.py
from .forms import EmployeeAdd  # 追加
...

def create(request):
    modelform_dict = {
        'title':'modelformテスト',
        'form': EmployeeAdd(),
    }
    return render(request, 'webtestapp/create.html', modelform_dict)
```

URLを割り当てないと表示できないので`urls.py`に処理を追加。

```testproject/webtestapp/urls.py
urlpatterns = [
    path('', views.index, name='index'),
    path('info', views.info, name='info'),
    path('create', views.create, name='create'),
]
```

これで最低限の表示ができるようになります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/2c666f79-4f76-a35d-fa48-ab542a10ee51.png)

見た目はダサくて送信ボタンを押しても何も起こらないのですが、表示はOKそう。
フォームに入力するための新規作成を行うためのページの見た目ができました。

### 入力データ処理
つぎに、送信ボタンを押した後の処理を作る必要がありますので`views`に処理を追加します。
送信ボタンを押した後のデータ登録処理を行う`if (request.method == 'POST'):`の部分を追加しました。

```testproject/webtestapp/views.py
from django.shortcuts import redirect  # 追加
from .forms import EmployeeAdd

def create(request):
    message = ''  # 初期表示ではカラ
    if (request.method == 'POST'):
        form = EmployeeAdd(request.POST)
        if form.is_valid():
            form.save()
            return redirect(to='/info')
        else:
            message = '再入力して下さい'
    modelform_dict = {
        'title':'modelformテスト',
        'form': EmployeeAdd(),
        'message': message,  # エラーメッセージ
    }
    return render(request, 'webtestapp/create.html', modelform_dict)
```

今回使用する`redirect`と先ほど作った`forms.py`に追加した`EmployeeAdd`をインポート忘れずに。

:::note info
「/create」にアクセスしたときの「表示」に関しては`GET`なので`if (request.method == 'POST'):`の条件判定で実行されず、formを作るための部品送ってるだけという感じです。「message」はエラーなったら文字入るのでデフォルトは空です。
:::

1. 送信ボタンを押された場合はPOSTで送信される
2. `form.is_valid()`はバリデーション機能です。ModelForm使うと`models.py`に書いた定義や制約に則ったデータかをチェックして問題なかったら`True`が返ってくる
3. `True`だと、formに入力されたデータを`save()`で保存する
4. `redirect`してあげないと送信ボタンを押して無事に登録されても画面に変化ないので`http://127.0.0.1:8000/info`に遷移してやる

そして`form.is_valid()`はバリデーションが`False`つまり、想定していないデータや構造のデータならエラーを表示させたいので文字を入れる

という流れです。

最後にエラーメッセージを表示するためにテンプレートにメッセージ追加。

```testproject/webtestapp/templates/webtestapp/create.html
...
<body>
  <h1>{{ title }}</h1>
  <p>{{ message }}</p>
  <table>
    <form action="{% url 'webtestapp:create' %}" method="post">
...
```

ではページを作成します。



こんなフォーム表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/ac07909e-0802-e4ea-584d-6ff3db514091.png)


管理画面で登録したのと同じようにデータが新規登録できます。
入力終わると`/info`にリダイレクト。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/5b9c114b-0337-03aa-d85b-8dbcef0f472d.png)


ちゃんと一つ増えてます。
## 6-2. CRUD (Update) ModelFormクラスで更新
次にDBに登録されたデータを更新する更新画面を作りましょう！

- `urls.py`にパスを追加
- `views.py`に`update`メソッドを追加
  - `EmployeemAdd`の定義からフォーム作る
  - `POST`だったら更新処理をする
- `update.html`という更新画面を作るテンプレート作成してフォームや表示を作る

### `urls.py`にパスを追加
URLに処理を紐づけます。

```testproject/webtestapp/urls.py
path('update/<int:num>', views.update, name='update'),
```

このように書くことにより更新画面を開くとURLが`http://127.0.0.1:8000/update/ID番号`とすることで`views`の`update`メソッドを開き、さらにID番号を`num`をいう名前でデータを入れることができます。

`Employee`には重複しない`id`がふられています。`num`を使うことで`Employee`の中の特定の`id`のデータを取得できるわけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/192910be-390c-e94f-e115-11afe26c31ac.png)

:::note info
上の図がEmployeeのデータの内容。idが自動で付加されていてこのidは重複しない。
:::

### `views.py`に`update`メソッドを追加
新規作成の時と似ているところもあります。

- `urls.py`で`<int:num>`と書いたのでnumにURLの最後につけた数字が入ってくる
- `employee_obj = Employee.objects.get(id=num)`のように更新する対象のデータ（DBに登録されていたデータ）を取得
- `employee = EmployeeAdd(request.POST, instance=employee_obj)`でデータを置き換えています。フォームで入力されたデータ`request.POST`でDBに登録されていたデータ`employee_obj`置き換え
- employeeはデータ置き換えられているのでデータに異常がないかチェックして保存、リダイレクト

:::note info
ついでに理解しなくても良いですが、employeeはModelFormです。新規作成のときはformという名前にしましたが、同じclassなのでformを作る機能もあります。Employeeモデルに機能追加されてる思えばいいかも
:::

```testproject/webtestapp/views.py
def update(request, num):
    message = ''
    employee_obj = Employee.objects.get(id=num)
    if (request.method == 'POST'):
        employee = EmployeeAdd(request.POST, instance=employee_obj)
        if employee.is_valid():
            employee.save()
            return redirect(to='/info')
        else:
            message = '再入力して下さい'
    update_dict = {
        'title': '登録情報更新画面',
        'id': num,
        'form': EmployeeAdd(instance=employee_obj),
        'message': message,
    }
    return render(request, 'webtestapp/update.html', update_dict)
```

`update_dict`の`id`は次に作るテンプレートの`{% url 'webtestapp:update' id %}`でこのように指定することで
`http://127.0.0.1:8000/update/1`のように数字を付加するのに使ってます。

`'form': EmployeeAdd(instance=employee_obj)`は`employee_obj`のように更新するデータを指定してやるとフォームに既存のデータが入力された状態で表示されるので、ユーザーに優しくなります。

:::note info
instance=employee_obj のように指定しないと更新ページを表示したときにフォームがすべてカラで始まるので更新したくないデータも更新してしまいやすいし、入力箇所も多くなる。
試しに「'form': EmployeeAddEmployeeAdd(),」に変えてみると違いわかるかも
:::

ついでに、

```testproject/webtestapp/views.py
...
employee_obj.name = request.POST['name']
employee_obj.mail = request.POST['mail']
.
.
employee_obj.save()
...
```

のように個別に更新する項目を書くことも可能です。ただし、`is_valid()`は使えないです。
場合によって使い分けてください。



そしてテンプレートを新規で作成。

```testproject/webtestapp/templates/webtestapp/update.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>{{ title }}</title>
  <link rel="stylesheet" href="{% static "css/style.css" %}" />
</head>
<body>
  <h1>{{ title }}</h1>
  <p>{{ message }}</p>
  <table>
    <form action="{% url 'webtestapp:update' id %}" method="post">
      {% csrf_token %}
      {{ form.as_table }}
      <tr>
        <td></td>
        <td><input type="submit" value="更新"></td>
      </tr>
    </form>
  </table>
</body>
</html>
```

さっきも触れたけど、`{% url 'webtestapp:update' id %}` は`urls.py`でid番号が入るようになるつまり`update/1`などになるということ。

試しに田中太郎に変更してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/0ee46345-be41-6936-2c62-c3bd1ac3b67d.png)

問題なく更新できたらユーザーの一覧が表示される`/info`にリダイレクトされて、名前が「田中太郎」に変わっていますね。

![info_page2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/a588a325-7cd2-87af-2aeb-6749d8fb0ecc.png)


これは必須でないですけど、更新するために`/update/1`などと更新するIDをアドレスバーに書かないと更新ページを使えないのは面倒ですね。なので`info.html`に更新リンクを付けよう思います。


```testproject/webtestapp/templates/webtestapp/info.html
  <body>
    <h1>{{ title }}</h1>
    <table>
      <tr>
      {% for h in header %}
        <th>{{ h }}</th>
      {% endfor %}
      </tr>

      {% for i in val %}
      <tr>
        <td>{{ i.id }}</td>
        <td>{{ i.name }}</td>
        <td>{{ i.mail }}</td>
        <td>{% if i.gender == False %}男{% endif %}
            {% if i.gender == True %}女{% endif %}</td>
        <td>{{ i.department }}</td>
        <td>{{ i.year }}</td>
        <td>{{ i.created_at }}</td>
        <td><a href="{% url 'webtestapp:update' i.id %}">更新</a></td>
      </tr>
      {% endfor %}
  </table>
  </body>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/011d9b21-b8f3-a858-8513-71c121a921b7.png)


「更新」リンクを一覧につけました。押すと更新画面がでるので少し便利です。

## 5.3 CRUD D(Delete) 追加
最後にデータ削除機能を付けます。更新よりわかりやすいと思います。
流れは同じ。削除画面は`delete.html`にしました。

まずは、URLを紐づける。

```testproject/webtestapp/urls.py
path('delete/<int:num>', views.delete, name='delete'),
```

削除処理を書く。`employee_obj.delete()`だけでOKですね。
他は以前に使ったような項目ですのでたどってもらえば何をやっているかはわかるはず。

```testproject/webtestapp/views.py
def delete(request, num):
    header = ['ID','名前','メール','性別','部署','社歴','作成日']
    message = ''
    employee_obj = Employee.objects.get(id=num)
    if (request.method == 'POST'):
        employee_obj.delete()
        return redirect(to='/info')
    delete_dict = {
        'title': '削除画面',
        'header': header,
        'id': num,
        'employee': employee_obj,
        'message': message,
    }
    return render(request, 'webtestapp/delete.html', delete_dict)
```

```Ctestproject/webtestapp/templates/webtestapp/delete.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>{{ title }}</title>
  <link rel="stylesheet" href="{% static "css/style.css" %}" />
</head>
<body>
  <h1>{{ title }}</h1>
  <p>{{ message }}</p>
  <table>
    <tr>
      {% for col in header %}
      <th>{{ col }}</th>
      {% endfor %}
    </tr>
    <tr>
      <td>{{ employee.id }}</td>
      <td>{{ employee.name }}</td>
      <td>{{ employee.mail }}</td>
      <td>{% if not employee.gender %}男{% endif %}
        {% if employee.gender %}女{% endif %}
      </td>
      <td>{{ employee.department }}</td>
      <td>{{ employee.year }}</td>
      <td>{{ employee.created_at }}</td>
      <td>
        <form action="{% url 'webtestapp:delete' id %}" method="post">
          {% csrf_token %}
          <input type="submit" value="削除">
        </form>
      </td>
    </tr>
  </table>
</body>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/ce280274-c039-7dff-91b0-493abb36fe7c.png)
普通に表示を作って、「削除」ボタンだけが他のページとの違いだけ。
`<form action="{% url 'webtestapp:delete' id %}" method="post">`更新の時と同じような感じです。

そして、削除を押して`id 6`の`John`を解雇しようと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/65979605-3891-7ff5-06d9-cd8670e929d7.png)

`John`が一覧から消えたのでデータがちゃんと削除されてそうです。

# 完成
長くなりましたが完成です。簡単なCRUDアプリを作成しました。
内容が分かりやすいので`関数ビュー`で作成しましたが`Modelビュー`もありDjangoの内容が分かっていればスマートに書けるので一般的に使われます。

今後`Modelビュー`の記事なども需要あれば作ろうかなと思います。


# :pushpin: 一連の記事

|  | 内容|
|:----:|:----:|
|**part1**|[簡単な文字表示アプリを作る](https://qiita.com/Bashi50/items/c686d30fa4fdba4582ad)|
|**part2**|[HTMLテンプレート表示](https://qiita.com/Bashi50/items/b9666030a058795c7dd8)|
|**part3**|["画像" "CSS" "Javascript"実装](https://qiita.com/Bashi50/items/3452bd1692fdd0ac6392)|
|**part4**|[フォーム送信](https://qiita.com/Bashi50/items/93da7a071b2e02dc21ed)|
|**part5**|**<font color="red">データベースの値取得・更新</font>　← ココ**|
|**番外編**|[Django AWS デプロイ](https://qiita.com/Bashi50/items/d5bc47eeb9668304aaa2)|
|**APIサーバー編**|[Django REST frameworkとReact #4](https://qiita.com/Bashi50/items/203505440049d759b694)|


# 参考
この章のコードは以下です。確認やコピペでどうぞ

https://github.com/kohishibashi/easy-django/tree/part5