Rails Model migration - izudon/izudon.github.io GitHub Wiki

マイグレーションの実行

  • マイグレーションの実行(最後まで実行)
    $ rails db:migrate
  • マイグレーションを1つ戻す
    $ rails db:rollback
  • マイグレーションをいくつか戻す
    $ rails db:rollback STEP=<N>
  • マイグレーションがどこまで実行されたかを確認
    $ rails db:version
  • マイグレーションをあるバージョンまで進ませる。
    $ rails db:migrate VERSION=<version>
  • マイグレーションを3つ戻し再度3つ実行する
    $ rails db:migrate:redo STEP=3
  • $ rails db:migrate:status

マイグレーションファイルの生成

  • マイグレーションファイルは rails g migration コマンドで生成できるが、 最終的には手作業目視確認にて完成させなければならない。
  • マイグレーションファイル名、クラス名、マイグレーションの内容には、 関連性があり、
    ある種の規則をが守られていなければエラーとなってマイグレーションできない。
  • 参考資料:
    Active Record マイグレーション - Railsガイド

コマンドライン

  • マイグレーションファイルの作成。
    $ rails g migration <マイグレーションクラス名> [[カラム1] [カラム2]...]
  • マイグレーションファイルの作成取消。
    $ rails d migration <マイグレーションクラス名>
  • カラム追加のマイグレーションファイル作成
    $ rails g migration AddPriceToSomethings price:integer
    • add_column を呼び出す change メソッドが定義されている。
  • カラム削除のマイグレーションファイル作成
    $ rails g migration RemovePriceFromSomethings price:integer
    • remove_column を呼び出す change メソッドが定義されている。

ケース(大文字小文字)

  • マイグレーション クラス名パスカルケース(PascalCase) 。
  • マイグレーション ファイル名スネークケース(snake_case) 。
  • テーブル名カラム名スネークケース(snake_case) 。
    • キャメルケース(camelCase)
    • パスカルケース(PascalCase)
    • スネークケース(snake_case)
    • ケバブケース(kebab-case)

マイグレーションクラス名

  • テーブル
    • 作成 CreateTttt
    • 削除 DropTttt
  • カラム
    • 追加 AddCcccToTttt
    • 削除 RemoveCcccFromTttt

カラムの列挙

  • title:string content:text user:references

マイグレーションファイルの編集

マイグレーションオブジェクト

  • Rails の認識ではDBは以下のようなオブジェクトからなり、
    それぞれ add したり remove したり、change したりするものである。
  1. テーブル系
    1. create_ drop_ rename_ change_ table
    2. create_ drop_ join_table
  2. カラム系
    1. add_ remove_ rename_ change_ column
    2. add_ remove_ reference (他テーブルへの参照(≒外部キー列))
    3. add_ remove_ timestamps (作成日時と更新日時)
  3. 制約系
    1. add_ remove_ rename_ index (インデックス(一意性制約も含む))
    2. add_ remove_ foreign_key (外部キー制約)
    3. add_ remove_ check_constraint (バリデーション)
  4. 属性系
    1. change_ column_comment (カラムへのコメント)
    2. change_ column_default (カラムのデフォルト値)
    3. change_ column_null (カラムの非ヌル制約)
    4. change_ table_comment (レーブルへのコメント)
create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)

主キーの指定

デフォルト

  • 何も指定しなければ
    * オートインクリメントされる
    * 整数型のカラムが
    id という名称で
    * 勝手に 作成される。
create_table :user do |t|
  t.string :email
  t.timestamps
end

カラム名を変えたい(id 以外にしたい)

  • create_tableid: オプションに、
    カラム名を指定すれば変えられる。
create_table :user, id: :member_no do |t|
  t.string :email
  t.timestamps
end

型を変えたい(文字列型などにしたい)

  • create_tableid: オプションに、
    型を指定すれば変えられる。
    • :string など、整数型以外を指定した場合には、
      オートインクリメントの指定も外れる。
    • :bigint など、integer 以外でも整数型を指定した場合には、
      オートインクリメントの指定は継続される。
create_table :user, id: :string do |t|
  t.string :email
  t.timestamps
end

もっと色々変えたいあるいは固定長文字列にしたいなど

  • create_table のオプションでは対応不可能になってくるので、
    id: オプションには false と指定し、
    列として明示的に定義し primary_key: true オプションをつける。
create_table :user, id: false do |t|
  t.string :id, limit: 10, primary_key: true
  t.string :email
  t.timestamps
end

複合主キー

  • create_tableprimary_key オプションに、
    カラム名を配列で指定する。
    • primary_key: true を複数カラムに指定するとエラーとなる。
    • Rails は複合主キーに対応していないという噂があったが、
      公式ドキュメント に堂々と乗っているのを先日発見した。
      ("Composit Primary Key"(複合主キー)の節)
  • 但し実行時にこんなワーニングが出るので、
    「非ヌル制約」+「(複合での)一意性制約」を併せて指定しておくと無難だと思う。
    WARNING: Active Record does not support composite primary key.
    authorizations has composite primary key. Composite primary key is ignored.
    
create_table :user, id: [:office_code, :desk_no] do |t|
  t.string  :office_code, limit: 10
  t.integer :desk_no
  t.string  :email
  t.timestamps
end
add_index :user, [:office_code, :desk_no], unique: true

参考:django は複合主キーをサポートしないらしい。

add_column(table_name, column_name, type, **options)

カラムを追加する(一般的な列に適した列追加メソッド)。

create_tablet.xxxxx の正体はこれ。

  • t.column はまさにこれ。
  • t.integer などは、
    t.column type: :integer t.column :integer などの略記。
    (第1引数であるから最初に指定するなら引数名を省ける)

オプション

  • type: 型を指定
  • primary_key: (単列での)主キー指定
    • 2つ以上のカラム => create_table メソッドの primary_key オプションを使う(既述)。
  • index: (単列での)インデックス要否
    • 2つ以上のカラム => add_index メソッドを使う。
    • true または add_index メソッドのオプションをそのまま指定する。
    • index: { unique: true } で「(単列での)一意性制約」となる。
  • limit: 長さの制約(文字列長やバイト長)
  • null: 非ヌル制約(false でヌル不可)
  • default: デフォルト値の指定

外部キー列を追加する(のに適した列追加メソッド)。

  • create_tablet.references の正体はこれ。
create_table :followers do |t|
  t.references :user, foreign_key: true
  t.references :follower, foreign_key: true
  t.timestamps
end

オプション

add_reference(table_name, ref_name, **options)
  • ref_name(第一引数)
    • 第一引数は「関係名」とでもいうべきラベルである(多分)。
    • 参照先テーブルへの明示的な指定がなければこれはテーブル名(単数形)と解される。
  • foreign_key: 外部キー制約(+インデックス)をつける。
    • true または add_foreign_key メソッドのオプションをそのまま指定する。
      • to_table: 参照先テーブル名を明示的に指定する。
      • column: カラム名を明示的に指定する。
  • index: インデックス要否(デフォルトは true
  • null: ヌル可否
  • polymorphic:
  • type:

(落とし穴)勝手に整数型にされる

  • type: を指定しないと、勝手に整数型にされ、
    参照先のテーブルの主キーが文字列型の場合など、型が一致せず、
    妙な不具合が起こることが、少なくとも SQLite3 を使った時起きた。
  • 相手型の主キーのそれを type: limit: には指定すべきと覚えておこう。

(落とし穴)ヌル許可なのにヌルを格納できない

  • null: true と指定しても、値が nil のとき撥ねられることがあった。
  • これは、レコード格納以前に、ヴァリデーション で撥ねられていた。
  • belongs_tooptional:true を指定することで、
    validates ではなく belongs_to の話)
    外部キーの値が nil でも、ヴァリデーションが通り、格納できるようになる。
    class Token < ApplicationRecord
      belongs_to :user, optional: true
    end

同じテーブルから2以上のカラムに外部キーが入る場合

  • to_table: に 参照先テーブル名を指定し、
    t.references の第一引数は「関係名」と位置付け、
    foreign_key: の値を(真偽値ではなく)ハッシュにする。
create_table :followers do |t|
  t.references :user, foreign_key: { to_table: :users }
  t.references :follower, foreign_key: { to_table: :users }
  t.timestamps
end
add_foreign_key(from_table, to_table, **options)
  • to_table:(第一引数) 参照先テーブル名を明示的に指定する。
  • primary_key: to_table の主キー列名の指定(デフォルトは id
    • to_table が複合主キーの場合は配列での指定。
  • column: from_table に追加されるカラム名を明示的に指定する。
    • to_table が複合主キーの場合は配列での指定。
  • index: インデックス要否(デフォルトは true
  • on_delete 親レコードの削除時の振る舞い
  • on_update 親レコードの更新時の振る舞い

フィクスチャでの指定

  • 公式ドキュメント ではとても素晴らしいことを書いているが、実際問題としては、
    テーブル名(単数系)で指定すると動作しないことがあると思う。
    カラム名で指定するのがよいかと思う。
    • エラー:
      one:
        id: 1
        user: user_one
    • うまくいく:
      one:
        id: 1
        user_id: user-one-id
add_index(table_name, column_name, **options)

インデックスを追加する。

一意性制約

インデックスの一種と位置付けられている。

unique: true をつける。

create_table :user, id: [:office_code, :desk_no] do |t|
  t.string  :office_code, limit: 10
  t.integer :desk_no
  t.string  :email
  t.timestamps
end
add_index :user, :email                  , unique: true
add_index :user, [:office_code, :desk_no], unique: true

フィクスチャの書き方

  • 文字列や数値
    • 引用符なしでそのまま書いて良い。
  • 日付時刻型
    • 何分前といった表現が使える(ERB)。
  • カラムがヌル
    • 値に null(または ~)を指定。
  • バイナリ値を指定したい
    • 値を !!binary | で始め、次行以降に Base64 形式で書く。
user_without_email:
  id: s00u9012
  name: "User without email"
  email: null # または '~'
  age: 19
  photo: !!binary |
    8f8ev6nzZQL9VvpPF9QlkuIlwGn5hj43/mPvAUbo48/zQ+bQ+GHLWLXWk8K+
    QW1Ug2Ax/el+6BMV0FjJA3UD2UUvpyaGD6juqIrincwnDYbC2MnLEIeab6R3                          
      :
    SLGHBGgh16j74mfEEstpw3g=                                                              
  updated_at: <%= Time.current.to_s(:db) %>
  created_at: <%= 3.days.ago %>
⚠️ **GitHub.com Fallback** ⚠️