はじめに
Railsでとあるレコードを削除すると、それに紐づいた他のモデルが外部キーで参照できなくなってエラーがでてしまった。
レコードを削除した時に関連するモデルのレコードを一緒に削除する方法を調べたので記載。
結論、dependent: :destroyを使えば解決した。
その他、周辺情報も調べて整理したのでまとめる。
間違っていたら教えていただけると幸いです。
#環境
- Ruby 2.5.1
- Rails 5.2.4
状況
モデルの相関関係を簡略化すると以下のような感じ。
ユーザーがカードを保有していて、そのカードとクーポンが多:多の関係でアソシエーションされている。
モデルのアソシエーションはこんな感じ。
# models/card.rb
has_many :cards_coupons
has_many :coupons, through: :cards_coupons
# models/cards_coupon.rb
belongs_to :card
belongs_to :coupon
# models/coupon.rb
has_many :cards_coupons
has_many :cards, through: :cards_coupons
dependent: :destroyを使う
上の状況でcardsのレコードを削除すると、cards_couponsテーブルに記載してあるcardカラムに外部キーが設定してあるため、エラーが起こる。
*基本的に外部キーに設定してあるカラムはnullではエラーがでる。nullでも大丈夫なようにするにはoptional: trueを追加すればいいのだが、DBに意味のないレコードが残るのが好ましくないため削除したい
(参考記事: https://blog.ryskit.com/entry/2018/01/27/195442)
レコードを削除するときに外部キーが設定されているcards_couponsのレコード削除
→cardsのレコード削除、couponsのレコード削除
とすればいいのかもしれないがやってらんない
そこであった便利な方法がdependent: :destroy
使い方は簡単。アソシエーションのオプションにこれを記載するだけ。
以下のようになる。
# models/card.rb
has_many :cards_coupons, dependent: :destroy #追加
has_many :coupons, through: :cards_coupons
# models/cards_coupon.rb
belongs_to :card
belongs_to :coupon, dependent: :destroy #追加
# models/coupon.rb
has_many :cards_coupons
has_many :cards, through: :cards_coupons
- 最初に追加したdependent: :destroyによって、cardsテーブルにあるレコードを削除した時にcards_couponsテーブルにあるレコードも一緒に削除してくれる
- 2番目に追加したdependent: :destroyによってcouponsテーブルのレコードも一緒に削除してくれる
めちゃ便利!!
こんな感じでdependent: :destroyはチェーンメソッドみたいに何個もつなげることが可能。
:delete_allなど関連する他のオプションとの違い
色々調べてると似たようなメソッドがいくつかあったので整理しておく。
主に以下の3つ
①destroy
②delete
③nullify
最後に表でまとめるのでそっちの方が見やすいかも
①destroy
destroyは上記記載の通りdestroyメソッドを行うので、チェーン的につなげられるのが特徴
####②delete
指定したDBに対して直接DELETEクエリが実行される。
そのため、destroyのようにチェーン的につなげられない。
deleteを使うメリットは処理速度の速さ
destroyは検索をかけた後に一つずつ削除するので処理が多くなるが、deleteは検索をかけて一致するデータをまとめて削除する
そのため、レコードの数が多く、チェーンさせる必要がない時などはdeleteの方が有効
ちなみにhas_manyに対してはdependent: :delete_all
belongs_to, has_oneに対してはdependent: :delete
####③nullify
指定したDBのレコードの外部キー部分にnulを与える。
関連するモデルのレコードは残しておきたいという時に便利
また、処理はnulによるupdateなので、deleteよりも軽いらしい
以上、3つの特徴を表でまとめるとこんな感じになる
チェーン的 | 処理速度 | レコードが残るか | |
---|---|---|---|
:destroy | ○ | △ | × |
:delete (has_manyは:delete_all) |
× | ○ | × |
:nullify | × | ◎ | ○ |