マイグレーションとシード機能
前回の続きで、モデルのより詳しい話。
参考
モデルの生成
モデルの作成手順
- モデルを生成する
- マイグレーションを行いモデルに対応するテーブルを作成する
- モデルの基本の検証を行う
- 動作検証に基づいて必要な機能を追加する
モデル生成コマンド
書式:$ rails g model モデル名 [モデル属性] [オプション] ...
モデル属性の書式:属性名[:データ型][:オプション]
モデル名は小文字単数形で、先頭が大文字のキャメル形式。
図書アプリに対して、以下の属性を持つUserモデルを生成・追加してみる。
- 名前(name)
- 住所(address)
- メールアドレス(email)
- 誕生日(birthday)
% bin/rails g model user name:string address:string email:string birthday:date Running via Spring preloader in process 2111 invoke active_record create db/migrate/20201012031057_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml
マイグレーションファイル
db/migrateディレクトリに生成された「数字14桁_create_users.rb」という名前のファイルがマイグレーションファイル。
# db/migrate/20201012031057_create_users.rb class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :name t.string :address t.string :email t.date :birthday t.timestamps end end end
t.timestampsという属性は、登録日(created_at)と更新日(updated_at)を生成する属性である。
rails db:migrateの実行
rails db:migrateを実行することで、このマイグレーションファイルに対応するusersテーブルを生成することができる。
% bin/rails db:migrate == 20201012031057 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0026s == 20201012031057 CreateUsers: migrated (0.0028s) =============================
マイグレーション
マイグレーションとは
Railsのマイグレーションとは、マイグレーションファイルを使ってRailsとは別の世界にあるデータベースを作成・更新するための作業である。
本来、データベースは十分理解した上で作成するべきであるが、Railsではマイグレーション機能によって、データベースの深い知識がなくても簡単に生成することができる。また、マイグレーションファイルを使うと、データベースの種類に依存せずにテーブルやスキーマなどを簡単に管理できる。
マイグレーションのコマンド
コマンド | 内容 |
---|---|
rails db:migrate | 未反映のマイグレーションファイルをもとにデータベースおよびschema.rbを更新する。オプションを指定して開発環境以外でも実行できる。バージョン指定も可 |
rails db:version | 現在の実行済のマイグレーションのバージョンを表示する |
rails db:migrate:status | マイグレーションの実行状況を一覧で表示する |
rails db:migrate:reset | データベースやスキーマを一度削除し、すべてのマイグレーションを再実行する |
rails db:setup | データベースの作成(db:create)、スキーマからのテーブル作成(db:schema:load)、初期データの登録(db:seed)を一連の作業として行う |
rails db:reset | データベースの削除(db:drop)、データベースの再作成(db:setup)を一連の作業として行う |
rails db:rollback [STEP=戻す数] | マイグレーションを1つ前のバージョンの状態に戻す(ロールバック)。データは削除される。STEPパラメータを指定すると、指定した数だけロールバックできる |
rails db:migrate:redo [STEP=戻す数] | ロールバックと再マイグレーションを一度に実行する。STEPパラメータで指定した数だけ戻して再実行する。戻したテーブルは再構成されるため初期状態になる |
rails db:schema:load | 現在のスキーマファイルからデータベースを作成する。データベースは削除され作り直される |
rails db:schema:dump | 現在のデータベースからスキーマを作成する |
rails db:drop | 現在のデータベースをすべて削除する |
マイグレーション名の付け方
モデルに必要になった属性を既存のテーブルに追加するなど、例外的な対応を行う場合には独自にマイグレーションファイルを作成する必要がある。
マイグレーション名は任意につけることが可能だが、以下の例のようにRailsの標準的な規約に従ってつけよう。
- テーブルを新規に作成する:create_テーブル名
- 既存テーブルに新しいカラムを追加する:addカラム名to_テーブル名
- 既存テーブルからカラムを削除する:removeカラム名from_テーブル名
- 多対多の関係の仲介テーブル「(has_belongs_to_many)用のテーブル」を作成する:create_join_tableモデル名モデル名
新規テーブルを作成する
特に支障がない限りは、create_テーブル名を使用せずにモデルから生成するほうがおすすめ。
例えば、Foodモデルを生成する。
% bin/rails g model food name:string Running via Spring preloader in process 2924 invoke active_record create db/migrate/20201012044713_create_foods.rb create app/models/food.rb invoke test_unit create test/models/food_test.rb create test/fixtures/foods.yml
# db/migrate/20201012044713_create_foods.rb class CreateFoods < ActiveRecord::Migration[6.0] def change create_table :foods do |t| t.string :name t.timestamps end end end
既存テーブルにカラムを追加する
Foodモデル(foodsテーブル)に新しい属性(カラム)を追加する。
% bin/rails g migration add_description_to_foods description:string Running via Spring preloader in process 3286 invoke active_record create db/migrate/20201012045306_add_description_to_foods.rb
生成されたマイグレーションファイルは以下の通り。
# db/migrate/20201012045306_add_description_to_foods.rb class AddDescriptionToFoods < ActiveRecord::Migration[6.0] def change add_column :foods, :description, :string end end
カラム属性を変更する
Foodモデルに追加したdescriptionを複数行の文字列(text)に対応させるようにする。
変更するための厳密な名前ルールはないが、次のような名前でマイグレーションファイルを生成することにする。
% bin/rails g migration change_datatype_description_of_foods Running via Spring preloader in process 3339 invoke active_record create db/migrate/20201012045854_change_datatype_description_of_foods.rb
以下のようなマイグレーションファイルが生成されるため、自分で記述する必要がある。
# db/migrate/20201012045854_change_datatype_description_of_foods.rb class ChangeDatatypeDescriptionOfFoods < ActiveRecord::Migration[6.0] def change end end
change_columnメソッドを使用し、以下のように書き換える。
class ChangeDatatypeDescriptionOfFoods < ActiveRecord::Migration[6.0] def change change_column :foods, :description, :text, default: "食事の内容" end end
書式:change_column :テーブル名, :カラム名, :データ型[, オプション]
descriptionの属性タイプをtextとし、初期値として"食事の内容"を設定している。
カラムを差し替える
カラム属性を変更する場合、古いカラムを削除して新しいカラムを追加する方法もある。
% bin/rails g migration change_attributes_of_foods Running via Spring preloader in process 3760 invoke active_record create db/migrate/20201012055302_change_attributes_of_foods.rb
# db/migrate/20201012055302_change_attributes_of_foods.rb class ChangeAttributesOfFoods < ActiveRecord::Migration[6.0] def up change_table :foods do |t| t.change :name, :string, default: "食事の名前" end end def down change_table :foods do |t| t.change :name, :string end end end
nameの初期値に"食事の名前"を設定している。
既存のカラム属性をインデックスにする
% bin/rails g migration add_index_email_to_users Running via Spring preloader in process 3953 invoke active_record create db/migrate/20201012061555_add_index_email_to_users.rb
# db/migrate/20201012061555_add_index_email_to_users.rb class AddIndexEmailToUsers < ActiveRecord::Migration[6.0] def change add_index :users, :email, unique: true end end
マイグレーションを実施すると、usersテーブルのemail属性に基づくユニークなインデックスが生成される。
スキーマ
マイグレーションを実行することで、データベースのテーブル、インデックスの生成、テーブル内のカラム追加などが行われ、最新のデータベースに基づいてスキーマファイルというファイルが作成・更新される。
スキーマファイル(schema.rb)
スキーマファイルには最新のデータベースのテーブル構造などを反映した情報が記載されている。
# db/schema.rb ActiveRecord::Schema.define(version: 2020_10_12_055302) do create_table "foods", force: :cascade do |t| t.string "name", default: "食事の名前" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.text "description", default: "食事の内容" end end
なお、idはスキーマ内には記載がないが、実際のテーブルには追加されている。
シード機能
マイグレーションによってテーブルを作成してもテーブルにはまだ何も登録されていない。アプリケーションを確認するため、テーブルにデータリソースを登録する必要があるとき、シード機能を使ってあらかじめテーブルに入れておきたい固有情報を登録することができる。
# db/seeds.rb Food.create(name: "ラーメン", description: "キングオブ麺") Food.create(name: "カレー", description: "スリランカカレー食べたい")
createメソッドを使って2件のデータを登録。
シード機能を実行し、結果をRailsコンソールで確認すると、
% bin/rails db:seed
irb(main):001:0> Food.all (0.5ms) SELECT sqlite_version(*) Food Load (0.2ms) SELECT "foods".* FROM "foods" LIMIT ? [["LIMIT", 11]] => #<ActiveRecord::Relation [#<Food id: 1, name: "ラーメン", created_at: "2020-10-12 06:44:18", updated_at: "2020-10-12 06:44:18", description: "キングオブ麺">, #<Food id: 2, name: "カレー", created_at: "2020-10-12 06:44:18", updated_at: "2020-10-12 06:44:18", description: "スリランカカレー食べたい">]> irb(main):002:0> Food.count (0.2ms) SELECT COUNT(*) FROM "foods" => 2
2件のデータが呼ばれていることがわかる。
例えば以下のようにseed.rbを記述すれば、100件のデータを簡単に入力することができる。
...(省略) 100.times do |n| Food.create!( name: "寿司#{n}", description: "寿司#{n}は日本文化です", ) end