はじめに
Rails6.1でreferences
を使って既存のテーブルを参照する新しいテーブルを作成しようとした際、エラーに遭遇しました。
エラーの内容とその原因・解決方法をまとめます。
エラーの概要
やりたいこと
既存のUser
テーブルを参照して新たにSocialLink
(SNSなどの外部サービスのリンクを保持する)テーブルを作成したい。
使用したマイグレーションファイル
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
t.references :user, null: false, foreign_key: true
t.string :type, null: false
t.string :url, null: false
t.timestamps
end
end
end
rails db:migrate実行時に発生したエラー
Mysql2::Error: Referencing column 'user_id' and referenced column 'id' in foreign key constraint 'fk_rails_a95eea6600' are incompatible.
エラーの原因
Railsのバージョンによる主キーや外部キーのデータ型の違いが原因でした。
- Rails5.1 より前: 主キーと外部キーは
integer
型 - Rails5.1 以降: 主キーと外部キーは
bigint
型
今回のケースは
-
User
テーブル:Rails 4で作成(id
がinteger
型) -
SocialLink
テーブル:Rails 6.1で作成(user_id
がbigint
型)
そのため、User
テーブルのid
カラムとSocialLink
テーブルのuser_id
カラムのデータ型が不一致となりエラーが生じていました。
解決策
以下の2つの解決策があります。
-
SocialLink
テーブルのuser_id
カラムをinteger
型に変換する -
User
テーブルのid
カラムをbigint
型に変更する
1. SocialLink
テーブルのuser_id
カラムをinteger
型に変換する
references
を使って外部キー制約をつける場合、以下のように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 :type, null: false
t.string :url, null: false
t.timestamps
end
end
end
もしくは、references
を使用せず、user_id
をinteger
型で定義し、add_foreign_key
で外部キーを別途つけても問題ありません。
class CreateSocialLinks < ActiveRecord::Migration[6.1]
def change
create_table :social_links do |t|
# user_id カラムを追加
t.integer :user_id, null: false
t.string :type, null: false
t.string :url, null: false
t.timestamps
end
# 外部キー制約を別途付与
add_foreign_key :social_links, :users
end
end
2. 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
あとは今まで通りreferences
を使って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 :type, null: false
t.string :url, null: false
t.timestamps
end
end
end
まとめ
- Rails5.1から主キーや外部キーのデータ型が
integer
からbigint
に変更された - そのためRails5.1以前に作成したテーブルを5.1以降で参照しようとすると型不整合によるエラーが生じる