基礎Ruby on Rails Chapter4 データベースとモデル

前 [基礎Ruby on Rails Chapter3 レイアウトテンプレート](https://qiita.com/tseno/items/64e4aab26a4cf217fad4)
次 [基礎Ruby on Rails Chapter4 データの保存](https://qiita.com/tseno/items/38fb7e743be58cc360b4)

## データベースの設定

### 接続の設定

- DBMSへの接続設定はconfig/database.ymlで行う。
- rails newコマンドは、DBMSを指定しないとSQLite3用の設定を作る。

```yml:config/database.yml
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3
```

### 3つのモード

- 開発 development 開発環境
- テスト test 自動テストのための環境
- 本番 production 本番環境

- database.ymlには、それぞれの環境用の設定が書いてある。
- config/environmentsディレクトリの下には、3つのモードごとの設定ファイルがある。（develop.rb、test.rb、production.rb）
- 環境変数によって切り替える。`export RAILS_ENV=production`を実行してから、railsコマンドを実行すると、本番モードの下で動く。

## データベースの作成

### データベースの作成

- 以下のコマンドで（develop、test環境の）dbを作成する。
- SQLLite3では自動的にデータベースができるが、MySQLやPostgreSQLでは必須。

```shell
$ bin/rails db:create
```

- 以下のコマンドで（本番環境の）dbを作成する。

```shell
$ bin/rails db:create RAILS_ENV=production
```

- 以下のコマンドでdbを削除する。（本番環境では`RAILS_ENV=production`を付ける）

```shell
$ bin/rails db:drop
```

### タイムゾーンの設定

```rb:config/application.rb（一部）

    # タイムゾーンの設定を追加
    config.time_zone = "Tokyo"
  end
end
```

## モデルの作成

- モデルを作成するには、`bin/rails g model モデル名`を実行する。
- モデル名は複数形にしてはいけない。
- entry_image、EntryImageとしてもEntryImageモデルができる。テーブル名はentry_images。

```shell
$ bin/rails g model member
      invoke  active_record
      create    db/migrate/20180918123930_create_members.rb
      create    app/models/member.rb
```

```rb:model/member.rb
class Member < ApplicationRecord
end
```


## マイグレーション

マイグレーションで、データベースの中にMemberモデルに対応するmembersテーブルを作成する。

### マイグレーションスクリプト

- 上記で作成した、`db/migrate/20180918123930_create_members.rb`がマイグレーションスクリプト。

```rb:db/migrate/20180918123930_create_members.rb
class CreateMembers < ActiveRecord::Migration[5.2]
  def change
    create_table :members do |t|

      t.timestamps
    end
  end
end
```

## membersテーブルの作成

上記スクリプトを以下のように修正する。

```rb:db/migrate/20180918123930_create_members.rb
class CreateMembers < ActiveRecord::Migration[5.2]
  def change
    create_table :members do |t|
      t.integer :number, null: false  # 背番号
      t.string :name, null: false # ユーザ名
      t.string :full_name # 本名
      t.string :email # メールアドレス
      t.date :birthday  # 生年月日
      t.integer :sex, null: false, default: 1 # 性別（1：男、2：女）
      t.boolean :administrator, null: false, default: false # 管理者フラグ

      t.timestamps
    end
  end
end
```

- migrateは、`bin/rails db:migrate`コマンドを実行すると、開発用のデータベースにテーブルmembersが作成され、カラムか追加される。
- 本番環境では、`bin/rails db:migrate RAILS_ENV=production`を実行する。

```sh
$ bin/rails db:migrate
== 20180918123930 CreateMembers: migrating ====================================
-- create_table(:members)
   -> 0.0018s
== 20180918123930 CreateMembers: migrated (0.0024s) ===========================
```

- `rb:db/schema.rb`に、migrateした時点でのデータベースの構造を再現するスクリプトが書き込まれる。

```rb:db/schema.rb
ActiveRecord::Schema.define(version: 2018_09_18_123930) do
  create_table "members", force: :cascade do |t|
    t.integer "number", null: false
    t.string "name", null: false
    t.string "full_name"
    t.string "email"
    t.date "birthday"
    t.integer "sex", default: 1, null: false
    t.boolean "administrator", default: false, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
end
```

## マイグレーションの詳細

### カラムの追加

- 以下を実行して、マイグレーションスクリプトを作成する。

```sh
$ bin/rails g migration AlterMembers
```

- 以下のように、`add_column :テーブル名, :カラム名, :カラムの型`を追記する。

```rb
class AlterMembers < ActiveRecord::Migration[5.2]
  def change
    # 以下を追記する。
    add_column :members, :phone, :string
  end
end
```

- `bin/rails db:migrate`を実行すると、新たに、phoneカラムが追加される。

### マイグレーションのバージョン

- マイグレーションスクリプトのファイル名の「年月日時分秒」は、マイグレーションのバージョンを表す。
- 古いバージョンに戻したい場合は、以下のようにバージョンを指定する。この前に行った変更が打ち消す操作が行われる。

```sh
$ bin/rails db:migrate VERSION=20180523132805
```

- マイグレーションのバージョンは、データベースのschema_migrationsテーブルで管理されている。
- schema_migrationsテーブルは、`bin/rails db:migrate`コマンドを実行すると自動的に作られる。
- `bin/rails db:migrate:status`コマンドを実行すると、現在のマイグレーションのバージョンを確認できる。upが付いているのが現在のマイグレーション。

```sh
$ bin/rails db:migrate:status

database: /mnt/c/Users/tseno/Desktop/rails/asagao/db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20180918123930  Create members
```

- データベース定義を古い定義に戻すには、`bin/rails db:rollback`コマンドも使える。
- 以下はマイグレーションを1つだけ取り消す。（ロールバック）

```sh
$ bin/rails db:rollback
```

- 以下はマイグレーションを3つ戻す。STEPオプションを使う。

```sh
$ bin/rails db:rollback STEP=3
```

### カラムの変更と削除

- マイグレーション用メソッド
    - add_column(テーブル名,カラム名,型,オプション) カラムの追加
    - rename_column(テーブル名,カラム名,新しい名前) カラム名の変更
    - change_column(テーブル名,カラム名,型,オプション) カラムの型の変更
    - remove_column(テーブル名,カラム名) カラムの削除

- 以下はnameカラムを、nicknameに名前を変更する例。

```rb
class AlterMembers < ActiveRecord::Migration
  def change
    rename_column :members, :name, :nickname
  end
end
```

- change_columnと、remove_columnは、changeメソッド内で使用すると、ロールバックできない。
  - upメソッドに、マイグレーションを進める処理を書く。
  - downメソッドに、取り消す処理を描く。

```rb
class AlterMembers < ActiveRecord::Migration
  def up
    rename_column :members, :name, :nickname
    change_column :members, :sex, :integer, null: false, default:2
  end

  def down
    change_column :members, :sex, :integer, null: false, default:1
    rename_column :members, :nickname, :name
  end
end
```

### インデックス

- インデックスの作成。`add_index :テーブル名, :カラム名, オプション`
- マイグレーションスクリプト、change、up、downメソッドで使用できる。
- add_indexオプション
    - unique: 重複禁止
    - name: インデックス名（省略すると、`テーブル名_カラム名_index`となる）

```rb
add_index :members, :name, unique: true, name: 'name_index'
```

- インデックスの削除。columnオプションでカラム名を指定するか、nameオプションでインデックス名を指定する。

```rb
remove_index :members, column: 'name'
remove_index :members, name: 'name_index'
```

### 開発中と本番でのマイグレーション

開発中にテーブル定義を変更する方法としては以下の2通りがある。

- 新しいマイグレーションスクリプトを追加し、マイグレーションを行う。
    - 本番稼働中など、データベースの破棄ができない場合
- 既存のマイグレーションスクリプトを書き換え、マイグレーションを最初からやり直す。
    - マイグレーションを最初からやり直すには、`bin/rails db:migrate:reset`コマンドを使う。

```sh
$ bin/rails db:migrate:reset
```

参考
[改訂4版 基礎 Ruby on Rails (IMPRESS KISO SERIES)](https://www.amazon.co.jp/%E6%94%B9%E8%A8%824%E7%89%88-Ruby-Rails-IMPRESS-KISO/dp/429500460X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1536666358&sr=1-1)