既存のテーブルをreferencesで参照して新たにテーブルを作成しようとしたところ、エラーにハマったので、備忘録としてまとめます。
やりたかったこと
この記事では既存のUserテーブルを参照して、新たにSocialLinkテーブル(SNSなど外部サービスのリンクを保持する)を実装すると仮定します。
以下のように references を用いたマイグレーションファイルを作成・実行しました。
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
t.references :user, null: false, foreign_key: true
t.string :social_network_type, null: false
t.string :social_network_url, null: false
t.timestamps
end
end
end
これを実行すると以下のエラーメッセージが出てしまいました、、。
Mysql2::Error: Referencing column 'user_id' and referenced column 'id' in foreign key constraint 'fk_rails_a95eea6600' are incompatible.
今まではこのやり方でテーブル追加ができていたので、理由が分からずだいぶ時間を取られました。
原因
id カラムのデータ型が Rails5.1前後で異なることが原因でした。
- Rails5.1 より前 → id カラムは int 型
- Rails5.1 以降 → id カラムは bigint 型
今回の場合、UserテーブルはRails4時代に作られたテーブルだったため id が int 型だったのに対し、 SocialLinkテーブルの social_link.user_id は bigint 型で作られてしまうため、型不整合によるエラーが起きてしまいました。
解決策
解決策は、social_link.user_id を int 型に変換するか、Userテーブルの id を bigint 型に変更するか、の2択です。
social_link.user_id を int 型に変換する場合
reference を使って外部キー制約をつける場合、以下のようにtype: :integer
を書けばOKです。
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
# type: :integer を追加
t.references :user, null: false, foreign_key: true, type: :integer
t.string :social_network_type, null: false
t.string :social_network_url, null: false
t.timestamps
end
end
end
もしくは、reference を使用せず、add_foreign_key
で外部キーを別途つけても問題ありません。
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
t.integer :user_id, null: false
t.string :social_network_type, null: false
t.string :social_network_url, null: false
t.timestamps
end
# 外部キー制約を作成
add_foreign_key :social_links, :users
end
end
Userテーブルの id を bigint 型に変更する場合
この場合、まずchange_column
でUserテーブルの id のデータ型を変更するマイグレーションファイルを実行します。
class ChangeUsersId < ActiveRecord::Migration[6.1]
def up
# int から bigint 型へ変更
change_column :users, :id, :bigint, auto_increment: true
end
def down
# rollback時に bigint から int 型へ戻す
change_column :users, :id, :int, auto_increment: true
end
end
あとは今まで通り reference を使ってSocialLinkテーブルを追加すればOKです。
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
t.references :user, null: false, foreign_key: true
t.string :social_network_type, null: false
t.string :social_network_url, null: false
t.timestamps
end
end
end
まとめ
reference で外部キー制約をつけるときに参照するテーブルがRails5.1より前に作成されている場合、id カラムのデータ型の不整合が起きるため変換する必要がある、と学びました。
コメント