Rails5.1以前に作ったテーブルに外部キー制約を貼る際の注意

既存のテーブルを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 カラムのデータ型の不整合が起きるため変換する必要がある、と学びました。

参考文献
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次