Rails 5.1のmigrationに関する変更点
Rails 5.1以前はmigrationを作成するとデフォルトでidカラムはintで作成されますが5.1からはこれがbigintに変更されました。
Restore the behaviour of the compatibility layer for integer-like PKs #27389
この変更で以下のようなmigrationが既に存在している場合に、
class CreateHoges < ActiveRecord::Migration[5.0]
def change
# hoges.idはintで作成される
create_table :hoges do |t|
t.string :name
t.timestamps
end
end
end
Rails5.1以降にアップデートするとFK制約を張れなくなりました。
class CreatePiyos < ActiveRecord::Migration[5.1]
def change
create_table :piyos do |t|
# piyos.hoge_idはbigintで作成される
t.references :hoge, foreign_key: true
t.timestamps
end
end
end
hoges.id
はintで作成されますが piyos.hoge_id
はbigintで作成されるので、別のタイプのカラム同士ではFK制約を張れないので rails db:migrate
が失敗します。
この問題の解決策を2つ紹介します。
hogesテーブルのidを変更せずにintでpiyos.hoge_idを作成する
piyos.hoge_idをintで作成する
class CreatePiyos < ActiveRecord::Migration[5.1]
def change
create_table :piyos do |t|
# piyos.hoge_idをintで作成する
t.references :hoge, foreign_key: true, type: :integer
t.timestamps
end
end
end
もしくは
piyos.hoge_idをintにして別途FK制約を張る
class CreatePiyos < ActiveRecord::Migration[5.1]
def change
create_table :piyos do |t|
# piyos.hoge_idをintで作成する
t.integer :hoge_id, default: nil
t.timestamps
end
# 別途FK制約を作成する
add_foreign_key :piyos, :hoges
end
end
add_foreign_keyについて
add_foreign_keyは名前の通りFK制約を作成します。
最初の引数で参照元のテーブル名、次の引数で参照先のテーブル名を指定します。
オプションとしてcolumnで参照元のカラム名を、primary_keyで参照先のカラム名を指定できます。
他のオプションとして参照先更新及び削除時の挙動を変更するon_updateとon_deleteもありますが、ここでは扱いません。
つまり、下の2つは同義です。
add_foreign_key :piyos, :hoges
add_foreign_key :piyos, :hoges, column: :hoge_id, primary_key: :id
piyos.hoge_idをpiyos.foo_idにしたい場合
add_foreign_key :piyos, :hoges, column: :foo_id
とすればOKです。
another_id等がPRIMARY KEYになってしまっている場合
class CreateHoges < ActiveRecord::Migration[5.0]
def change
create_table :hoges, id: false do |t|
t.string :name
t.column :another_id, 'INTEGER PRIMARY KEY AUTO_INCREMENT'
t.timestamps
end
end
end
以下のようにすればOKです。
class CreatePiyos < ActiveRecord::Migration[5.1]
def change
create_table :piyos do |t|
t.integer :hoge_another_id
t.timestamps
end
add_foreign_key :piyos, :hoges, column: :hoge_another_id, primary_key: :another_id
end
end
id以外をPRIMARY KEYにしたりPRIMARY KEY以外にFK制約を張るのはRails wayから外れるのでやむを得ない事情があっても控えましょう。
hogesテーブルのidをintからbigintに変更してからpiyosを作成する
hoges.idをintからbigintに変更する
class ChangeHogesId < ActiveRecord::Migration[5.1]
def up
# intからbigintへの変更用
change_column :hoges, :id, :bigint, auto_increment: true
end
def down
# rollback時のbigintからintへの変更用
change_column :hoges, :id, :int, auto_increment: true
end
end
普通にpiyosを作成する
class CreatePiyos < ActiveRecord::Migration[5.1]
def change
create_table :piyos do |t|
t.references :hoge, foreign_key: true
t.timestamps
end
end
end
終わりに
いかがだったでしょうか、Railsは基本後方互換性を保ちながら開発が進められていますが、Webの世界は流れが早いのでこういった破壊的な変更が稀に起こるのは仕方のない事でしょう。
この記事によってRDBにFK制約を張らずにModel側でのみassociationを設定してしまうRailsエンジニアが1人でも減る事を祈ります。