コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.5 - Model and CRUD -

# はじめに
第５回は、Modelを作って遊んでみます！
Modelはデータベースと密に関係していますので、メソッドをつかってCRUDを試してみます。

# 前回のソースコード
前回のソースコードはこちらに格納してます。今回のだけやりたい場合はこちらからダウンロードしてください。

- [at946/rails_on_docker_handson at vol.04](https://github.com/at946/rails_on_docker_handson/tree/vol.04)

# Userモデルを作ろう
今回作るModelは以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254216/b5175fdf-5701-9c06-f92c-d95291dfc94f.png)
ER図を描くツールは[dbdiagram.io](https://dbdiagram.io/home)を使ってます。Entityしか書いてないから何ともですが、便利なツールです。

Userモデルがどんなモデルかといえば、Integer型の`id`をprimary keyとして、String型の`name`, `email`、Datetime型（年月日時分秒）の`created_at`（作成日時）, `updated_at`（更新日時）を持っています。

## rails generate model
モデルを作成するコマンドは`rails generate model`です。Modelに必要なファイルとそのModelのデータをDBに登録するためにDB側にテーブルを作る必要があるのでマイグレーションファイルを作成してくれます。

`rails generate model NAME [field:type field:type]`が型ですね。
`NAME`がモデルの名前です。`field`がモデルのattribute（属性）、`type`が型です。

早速、ER図のモデルを作ってみましょう！

```
$ docker-compose up -d
$ docker-compose exec web ash
```

```
# rails generate model user name:string email:string
```
注目点は`id`, `created_at`, `updated_at`のことはコマンドで定義していないところです。ここはあとで説明。

## マイグレーションファイル
先ほどのコマンド実行でマイグレーションファイルが生成されています。

```rb:db/migrate/YYYYMMDDhhmmss_create_user.rb
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end
end
```
Scaffoldの時も実は同じようにファイルができていたんですが、中身を見るのは初めてですね。
`rails db:migrate`をするとRailsがこのファイルを読み込んでDBにSQLを発行してくれているんですね。
先ほどコマンドで定義した`name`, `email`と一緒に`timestamps`というものがあります。これが`created_at`, `updated_at`を作ってくれるやつでモデルを作成するときにRailsがデフォルトで付けてくれています。

また、`create_table`はデフォルトでprimary keyとしてInteger型の`id`を付けてくれます。
なので、このままマイグレーションファイルを`db:migrate`すれば先ほどのER図通りのテーブルを作成してくれます。

```
# rails db:migrate
```

# Userモデルで遊んでみる
ここからは作成したUserモデルを使ってCRUDで遊んでみます。
CRUDとはデータを操作する上で基本となる`Create`（作成）、`Read`（参照）、`Update`（更新）、`Delete`（削除）の頭文字をとったものです。Railsの場合はSQL文をコーディングするのではなく、モデルのメソッドを使うだけでCRUDができちゃうのでそれを体感しましょう！

ここからはRails consoleを使っていきます。これでRailsアプリケーションとコマンドラインで対話式にやりとりができます。
Rails consoleはそのままでもいいのですが、`pry`というツールをインストールすることでRails consoleがみやすくなるのでそうします。

```diff:Gemfile
...
group :development, :test do
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+ gem 'pry-rails'
end
...
```

gemを追加でインストールする場合は、再度Docker imageをビルドします。今立っているコンテナは古いイメージをもとに作られたコンテナなので一回落として、新しくビルドしたイメージで再度コンテナを作ってあげましょう。

```
# exit
```

```
$ docker-compose down
$ docker-compose build
$ docker-compose up -d
```

コンテナを起動できたら、コンテナの中で`rails console`コマンドを使ってRails consoleを立ち上げましょう！

```
$ docker-compose exec web ash
```

```
# rails console
Running via Spring preloader in process 335
Loading development environment (Rails 6.0.2.1)
[1] pry(main)>
```
Rails console内では接頭に`>`がつくので、`>`がついている時はRails consoleの中でコマンドを実行しているんだなと思ってくださいね。

## Create
モデルの作成の方法は大きく２種類あります。

- `new`メソッドでモデルオブジェクトを作成し、`save`メソッドでデータ保存する
- `create`メソッドでオブジェクト作成とデータ保存を同時に行う

### Create 1: `new`+`save`
例で`tanaka@sample.com`のメアドの`Taro Tanaka`さんを作ってみましょう！

```pry
> user = User.new(name: "Taro Tanaka", email: "tanaka@sample.com")
=> #<User:0x000056543a28cfa0
 id: nil,
 name: "Taro Tanaka",
 email: "tanaka@sample.com",
 created_at: nil,
 updated_at: nil>
```
```pry
> user.save
   (0.6ms)  BEGIN
  User Create (11.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "Taro Tanaka"], ["email", "tanaka@sample.com"], ["created_at", "2020-03-09 13:02:13.882889"], ["updated_at", "2020-03-09 13:02:13.882889"]]
   (2.7ms)  COMMIT
=> true
```
まず、`User.new`で例の属性を持つUserモデルオブジェクトを作成し変数`user`に代入しています。

その後、`save`メソッドを実行。実行後のコンソールからDBにINSERTしているのがわかると思います。
RailsではModelのメソッドを使うことでとても簡単にSQLの操作ができるようになります。
`save`メソッドはデータ作成に成功したら`true`を失敗したら`false`を返却するメソッドです。

また、<model>.<attribute>の形で属性の情報を取得したり設定したりできるので、以下のようなやり方でもデータを作成することができます。
`yamada@sample.com`のメアドの`Hanako Yamada`さんを作ってみます！

```pry
> user = User.new
=> #<User:0x000056543a58a298
 id: nil,
 name: nil,
 email: nil,
 created_at: nil,
 updated_at: nil>
```
```pry
> user.name = "Hanako Yamada"
=> "Hanako Yamada"
```
```pry
> user.email = "yamada@sample.com"
=> "yamada@sample.com"
```
```pry
> user.save
   (0.8ms)  BEGIN
  User Create (1.6ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "Hanako Yamada"], ["email", "yamada@sample.com"], ["created_at", "2020-03-09 13:06:00.252962"], ["updated_at", "2020-03-09 13:06:00.252962"]]
   (1.0ms)  COMMIT
=> true
```

### Create 2: create
`create`メソッドは`new`メソッドと`save`メソッドを同時に行うメソッドっす。
`john@sample.com`のメアドの`John Smith`を作ります。突然の外国人ですが、『John Smith』は日本でいう『名無しの権兵衛』です。

```pry
> User.create(name: "John Smith", email: "john@sample.com")
   (0.6ms)  BEGIN
  User Create (1.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["name", "John Smith"], ["email", "john@sample.com"], ["created_at", "2020-03-09 13:06:23.424103"], ["updated_at", "2020-03-09 13:06:23.424103"]]
   (2.2ms)  COMMIT
=> #<User:0x000056543a8b3320
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
`create`は基本的には`new`+`save`なのですが、作成が成功した場合はそのモデルオブジェクト、失敗した場合は`false`を返却するところが大きく違うポイントです。

## Read
データを確認するメソッドはかなりあります。ここでは特に利用頻度が高いであろうメソッドをご紹介します。

### all
`all`メソッドはそのモデルの全てのレコードをオブジェクトの配列として取得します。

```console
> User.all
  User Load (17.1ms)  SELECT "users".* FROM "users"
=> #<User:0x000056543a8b3320
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
[8] pry(main)> User.all
  User Load (2.8ms)  SELECT "users".* FROM "users"
=> [#<User:0x000056543a958320
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x000056543a958140
  id: 2,
  name: "Hanako Yamada",
  email: "yamada@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>,
 #<User:0x000056543a958050
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>]
```

### find
`find`メソッドはprimary keyの値を指定して1件のオブジェクトを取得するメソッドです。

```pry
> User.find(1)
  User Load (1.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<User:0x000056543afed488
 id: 1,
 name: "Taro Tanaka",
 email: "tanaka@sample.com",
 created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>
```
マッチするレコードがない場合は、ActiveRecord::RecordNotFound Exceptionを発生させます。

```pry
> User.find(5)
  User Load (3.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 5], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=5
from /usr/local/bundle/gems/activerecord-6.0.2.1/lib/active_record/core.rb:177:in `find'
```

### find_by
`find_by`メソッドは指定したカラムの条件にマッチした1件のオブジェクトを取得するメソッドです。

```pry
> User.find_by(email: "john@sample.com")
  User Load (2.8ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "john@sample.com"], ["LIMIT", 1]]
=> #<User:0x00005654375fba40
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
マッチするレコードが存在しない場合はnilを返却します。

```pry
> User.find_by(email: "hoge@sample.com")
  User Load (2.7ms)  SELECT "users".* FROM "users" WHERE "users"."email" = $1 LIMIT $2  [["email", "hoge@sample.com"], ["LIMIT", 1]]
=> nil
```
また、`find_by`メソッドでは複数の条件全てにヒットする1レコードを取得する書き方もできます。

```pry
> User.find_by(name: "John Smith", email: "john@sample.com")
  User Load (2.2ms)  SELECT "users".* FROM "users" WHERE "users"."name" = $1 AND "users"."email" = $2 LIMIT $3  [["name", "John Smith"], ["email", "john@sample.com"], ["LIMIT", 1]]
=> #<User:0x00005654388b5b68
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
こんな感じでユーザーを特定することができます！

### where
`where`メソッドはSQLのWHERE句と同様、レコードの検索条件を指定し、条件にマッチしたレコードをオブジェクトの配列として取得します。
条件はハッシュ型で書くのがわかりやすくてオススメ。

#### 等値条件
あるカラムが特定の値と同一であるレコードを取得する場合、単に`key: value`の形で条件を指定するだけです。

```pry
> User.where(id: 1)
  User Load (1.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1  [["id", 1]]
=> [#<User:0x0000565439591ee0
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>]
```

#### 範囲条件
valueには範囲を指定することもできます。Rubyでは`0 ≦ x ≦ 2`を`0..2`、`0 ≦ x < 2`を`0...2`と表現します。

```pry
> User.where(id: 1..2)
  User Load (2.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" BETWEEN $1 AND $2  [["id", 1], ["id", 2]]
=> [#<User:0x0000565439e6a940
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x0000565439e6a7d8
  id: 2,
  name: "Hanako Yamada",
  email: "yamada@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>]
```

#### サブセット条件
valueには配列を指定することも可能です。

```pry
> User.where(id: [1, 3])
  User Load (2.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN ($1, $2)  [["id", 1], ["id", 3]]
=> [#<User:0x000056543a0993b0
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x000056543a0991a8
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>]
```

#### NOT条件
`where.not`メソッドを使えば、条件にヒットしないものを検索することもできます。

```pry
> User.where.not(id: 1)
  User Load (2.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" != $1  [["id", 1]]
=> [#<User:0x000056543a2a82f0
  id: 2,
  name: "Hanako Yamada",
  email: "yamada@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>,
 #<User:0x000056543a2a8228
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>]
```

#### AND条件
複数の条件にマッチするレコードを取得したい場合は、ハッシュの組み合わせを増やせばいいだけです。

```pry
> User.where(name: "Taro Tanaka", email: "tanaka@sample.com")
  User Load (1.4ms)  SELECT "users".* FROM "users" WHERE "users"."name" = $1 AND "users"."email" = $2  [["name", "Taro Tanaka"], ["email", "tanaka@sample.com"]]
=> [#<User:0x000056543a3f8330
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>]
```

#### OR条件
複数の条件のうち、どれか一つでも当てはまるレコードを取得したい場合は`or`メソッドを使います。これはやや面倒（直感的でない）っす。

```pry
> User.where(id: 1).or(User.where(email: "john@sample.com"))
  User Load (3.2ms)  SELECT "users".* FROM "users" WHERE ("users"."id" = $1 OR "users"."email" = $2)  [["id", 1], ["email", "john@sample.com"]]
=> [#<User:0x000056543a4c1230
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x000056543a4c1000
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>]
```

### order
`order`メソッドは並び順を指定してくれます。使い方は並び順の条件にしたいカラムを指定するだけで昇順で並び替えてくれます。

```pry
> User.order(:email)
  User Load (3.5ms)  SELECT "users".* FROM "users" ORDER BY "users"."email" ASC
=> [#<User:0x000056543a686728
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>,
 #<User:0x000056543a686570
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x000056543a6863b8
  id: 2,
  name: "Hanako Yamada",
  email: "yamada@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>]
```
メアド昇順に並び変わってますね。

降順に並べる場合は`desc`を使います。（昇順も同じように`asc`を指定して表現することもできます）

```pry
> User.order(email: :desc)
  User Load (2.5ms)  SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
=> [#<User:0x000056543a8ddda0
  id: 2,
  name: "Hanako Yamada",
  email: "yamada@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>,
 #<User:0x000056543a8ddb98
  id: 1,
  name: "Taro Tanaka",
  email: "tanaka@sample.com",
  created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>,
 #<User:0x000056543a8dda58
  id: 3,
  name: "John Smith",
  email: "john@sample.com",
  created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
  updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>]
```
逆順になってる。

### first
`first`メソッドはprimary keyの順番で最初の1件のオブジェクトを取得するメソッドです。

```pry
> User.first
  User Load (2.1ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> #<User:0x000056543aa14570
 id: 1,
 name: "Taro Tanaka",
 email: "tanaka@sample.com",
 created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>
```
結果は先ほどの`find`メソッドと同じTaro Tanakaがヒットしていますが、SQL文に違いがあることがわかります。`find_by`が`WHERE`で検索しているのに対して、`first`は`ORDER`で検索してます。

また、`order`メソッドと組み合わせることで、primary key以外の属性に対しても一番先頭のオブジェクトを取得することができます。こっちの方が便利な気がする。

```pry
> User.order(:email).first
  User Load (2.2ms)  SELECT "users".* FROM "users" ORDER BY "users"."email" ASC LIMIT $1  [["LIMIT", 1]]
=> #<User:0x000056543aab5e98
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```

### last
なんとなくメソッド名からわかりますね。`first`の逆、一番後ろのモデルオブジェクトを取得するメソッドです。

```pry
> User.last
  User Load (1.4ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> #<User:0x000056543ab34fb8
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
もちろん`order`と組み合わせて使うことも可能！

```pry
> User.order(:email).last
  User Load (2.4ms)  SELECT "users".* FROM "users" ORDER BY "users"."email" DESC LIMIT $1  [["LIMIT", 1]]
=> #<User:0x000056543abc4a78
 id: 2,
 name: "Hanako Yamada",
 email: "yamada@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>
```

## Update
データの更新の方法も大きく2つあります。どちらの場合もまずfindやfind_byを使って単一のオブジェクトを取得します。で属性のデータを変更したあとに`save`メソッドを使って更新するか、`update`メソッドで更新するかです。

### 属性を更新して`save`
一つ目の方法は取得したオブジェクトの属性の値を変更してCreateと同じように`save`メソッドを使うことです。
`Taro Tanaka`さんの`email`を`taro@sample.com`に変更してみましょう！

```pry
> user = User.find(1)
  User Load (2.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<User:0x000056543ac45060
 id: 1,
 name: "Taro Tanaka",
 email: "tanaka@sample.com",
 created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:02:13 JST +09:00>
```
```pry
> user.email = "taro@sample.com"
=> "taro@sample.com"
```
```pry
> user.save
   (0.4ms)  BEGIN
  User Update (3.5ms)  UPDATE "users" SET "email" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["email", "taro@sample.com"], ["updated_at", "2020-03-09 13:17:32.314299"], ["id", 1]]
   (1.6ms)  COMMIT
=> true
```
```pry
> User.find(1)
  User Load (4.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<User:0x000056543adb0c38
 id: 1,
 name: "Taro Tanaka",
 email: "taro@sample.com",
 created_at: Mon, 09 Mar 2020 13:02:13 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:17:32 JST +09:00>
```
`email`変わってますね。`updated_at`も自動的に更新されてます！

### `update`メソッドを使う
もう一つの方法はupdateメソッドを使う方法です。
今度は、`Hanako Yamada`さんの`email`を`hanako@sample.com`に変更してみます！

```pry
> user = User.find(2)
  User Load (2.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
=> #<User:0x000056543ae6e990
 id: 2,
 name: "Hanako Yamada",
 email: "yamada@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:00 JST +09:00>
```
```pry
> user.update(email: "hanako@sample.com")
   (0.5ms)  BEGIN
  User Update (1.3ms)  UPDATE "users" SET "email" = $1, "updated_at" = $2 WHERE "users"."id" = $3  [["email", "hanako@sample.com"], ["updated_at", "2020-03-09 13:18:46.735580"], ["id", 2]]
   (0.8ms)  COMMIT
=> true
```
```pry
> User.find(2)
  User Load (4.8ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
=> #<User:0x000056543af50368
 id: 2,
 name: "Hanako Yamada",
 email: "hanako@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:00 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:18:46 JST +09:00>
```
`update`メソッドの場合は更新したい属性と値を括弧の中で指定します。
`update`メソッドも`create`メソッドと同じで、データ保存に成功した場合はそのモデルオブジェクトが結果として返却されていますね。

## Delete
データの削除には`destroy`メソッドを使います。今回は`John Smith`さんが退会した、みたいな感じでデータ削除してみましょう。

```pry
> user = User.find(3)
  User Load (0.9ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
=> #<User:0x000056543b009660
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
```pry
> user.destroy
   (0.7ms)  BEGIN
  User Destroy (2.1ms)  DELETE FROM "users" WHERE "users"."id" = $1  [["id", 3]]
   (3.1ms)  COMMIT
=> #<User:0x000056543b009660
 id: 3,
 name: "John Smith",
 email: "john@sample.com",
 created_at: Mon, 09 Mar 2020 13:06:23 JST +09:00,
 updated_at: Mon, 09 Mar 2020 13:06:23 JST +09:00>
```
```pry
> User.find(3)
  User Load (1.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=3
from /usr/local/bundle/gems/activerecord-6.0.2.1/lib/active_record/core.rb:177:in `find'
```
あっけないですね。
最終的にもともと`John Smith`さんに割り当てられていた`ID=3`で検索してみたところ、ActiveRecord::RecordNotFoundの例外が発生していることから`John Smith`さんがちゃんと削除されていることがわかります。

# 後片付け
今日のデータをきれいにしておきましょう。今回もDBの再作成で。

```pry
> quit
```
`quit`コマンドでRails consoleから抜け出せます。

```
# exit
```
`exit`コンテナからぬけまして、

```
$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset
```
コンテナを停止して、`rails db:migrate:reset`を実行っと。

# まとめ
今回は、Modelの作成と基本的なモデル（データ）の操作をやってみました。
ここは僕の中でRailsの使いやすいところだなーと思っているのですが、SQLを隠してくれているんですよね。モデルのメソッドって形でデータのCRUDできるのは本当に使いやすい。

次回はモデルにバリデーションをつけていこうと思います。今のままだと`name`や`email`に値がなくても保存できちゃうんですよね。
あと、Userモデルにセキュアなパスワードの属性を追加していきます。単にパスワードをカラム追加してしまったら万が一データが盗まれた時に大変な個人情報流出です。Railsではセキュアなパスワードを扱うために`has_secure_password`メソッドが用意されているのでその使い方を紹介します。

では、次回も乞うご期待！ここまでお読みいただきありがとうございました！

Next: [コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.6 - Model validation - - Qiita](https://qiita.com/at-946/items/f83dbdf12d2b286d88b7)

# 本日のソースコード

- [at946/rails_on_docker_handson at vol.05](https://github.com/at946/rails_on_docker_handson/tree/vol.05)

# Reference
- [Ruby on Rails チュートリアル：実例を使って Rails を学ぼう](https://railstutorial.jp/)
- [Railsタイムゾーンまとめ - Qiita](https://qiita.com/aosho235/items/a31b895ce46ee5d3b444)
- [rails-i18n/ja.yml at master · svenfuchs/rails-i18n](https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml)
- [もう迷わない！CSS Flexboxの使い方を徹底解説 | Web Design Trends](https://webdesign-trends.net/entry/8148)
- [【2019年版】Google Fontsの使い方：初心者向けに解説！](https://saruwakakun.com/html-css/basic/google-fonts)

# Other Hands-on Links
- https://github.com/at946/rails_on_docker_handson/tree/master