Ruby
Rails
MySQL

外部キーを持つデータのdestroyを行うための設定

前提

Railsのアプリケーションで、ユーザがイベントを作成して、他ユーザがそのイベントに参加するというような機能を持っているとします。

今回作成したモデルは以下の3つ

  • User / ユーザ
  • Event / イベント
  • Participation / ユーザとイベントの関連付けテーブル

各モデル同士の関連付けは、以下の通り。

/app/model/users.rb
class User < ApplicationRecord
  has_many :events, foreign_key: :user_id
  has_many :participations, foreign_key: :user_id
end   
/app/model/events.rb
class Event < ApplicationRecord
  belongs_to :user, optional: true
  has_many :participations, foreign_key: "event_id"

end
/app/model/participations.rb
class Participation < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :event, optional: true

end

エラー内容

Eventを削除しようとした際に、以下のエラーが発生しました。

ActiveRecord::StatementInvalid: Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails

原因:外部キーの参照整合性が原因

そもそも外部キーの目的は、主キーを持つテーブルと、外部キーのテーブルをリンクを作ることで、主キー側のテーブルのデータに対する変更へ制限を加えることです。

今回のように、Eventが削除された際に、Eventの主キーを参照するParticipationテーブルのデータは参照先を持たないデータとなってしまう恐れがあります。外部キーの設定をすることで、この不具合を未然に防いでいます。

外部制約キーの設定

対応としては、以下のように、destoryで外部キーとして設定しているレコード削除させるように、dependent: :destroyを設定してあげればよいです。今回はEventだけでなく、userに対しても同様の設定を行なっています。

/app/model/users.rb
class User < ApplicationRecord
  has_many :events, foreign_key: :user_id, dependent: :destroy
  has_many :participations, foreign_key: :user_id, dependent: :destroy

end   
/app/model/events.rb
class Event < ApplicationRecord
  belongs_to :user, optional: true
  has_many :participations, foreign_key: "event_id", dependent: :destroy

end
/app/model/participations.rb
class Participation < ApplicationRecord
  belongs_to :user, optional: true
  belongs_to :event, optional: true

end

参考文献

Foreign Key Constraints / Microsofts

外部キー制約でハマったので(rails)