🙂

削除できない原因は外部キー制約だった。

2020/10/22に公開

概要


  • 投稿を削除しようとしたらエラーが発生して削除できなかった
  • PG::ForeignKeyViolation: ERROR: update or delete on table
  • 外部キー制約が原因

実装の内容としては、

questionテーブル:1
solutionテーブル:多
この様に質問と回答の
2つのテーブルがあり、1対多の関係で紐づいていた。

PG::ForeignKeyViolation: ERROR: update or delete on table


エラーが起きた手順は以下の流れです。

  1. 質問を投稿
  2. 1でした投稿に回答を作成
  3. 質問を削除
  4. エラー発生
PG::ForeignKeyViolation: ERROR: update or delete on table

というエラーが発生して削除ができなかった。
(見たことがなかったので結構びびった。笑)

調べてみると、
外部キー制約というルールが原因でした。

外部キー制約を簡単に説明すると、
「他のテーブルのデータに参照(依存)するようにカラムにつける制約」

今回の件だと、
質問を削除してしまうと、それに紐づく回答も削除されてしまうけど大丈夫?

というエラーですね。

解決策


え?じゃあ回答がついてる質問は削除できないの?

と思いましたがそんなことはないですね。笑

  • 各モデルに許可するコード書く。
  • dependent オプションを使う。

dependent オプションについて詳しくはこちら&参照
https://qiita.com/jnchito/items/3456ce734ef41d216ecd

destroy: 親と一緒に子レコードも削除する。(無理心中パターン)
delete_all: 親と一緒に子レコードも削除する。ただし、直接DBのレコードを削除するので、子レコードのコールバック処理は実行されない。
nullify: 子レコードの外部キーを NULL 更新する。(みなしごパターン)
restrict_with_exception: 子レコードがある場合は ActiveRecord::DeleteRestrictionError が発生する。(引き留めパターン)
restrict_with_error: 子レコードがある場合は削除できず、親レコードにエラー情報が付加される。(引き留めパターン)

今回だと

class Question < ApplicationRecord
  has_many :solutions, dependent: :destroy
end

このようにモデルに追記することで削除できるようになった。
質問を消すと、その質問に紐づく回答も一緒に消して大丈夫だよ。というような意味ですね。

これで一件落着。

まとめ


アソシエーションがあるテーブルでは
外部キー制約も意識しないといけない。

dependent オプションの使い分けがまだ理解できていないのと
外部キー制約はこれからも使うことが多いと思うので
徐々に理解していく必要がある。

こちらの記事もオススメです。
https://teratail.com/questions/20137

Discussion