【Rails】ActiveRecordの:dependent使い分けまとめ【:destroy, :delete, :nullify】

ActiveRecordのdependentオプションとは
dependentオプションとは、モデルがdestroy
されたときの、関係づけされたモデルに対する挙動を定義するものです。
たとえばこちらのコードの場合、Heroがdestroy
されたときの、Heroに関連づけられたWeaponに対する挙動を定義しています。
class Hero < ApplicationRecord
has_one :weapon, dependent: :nullify
end
dependentで設定できる値は、大きく分けて4つあります。
dependent: destroy
こちらを指定すると、destroy
時に関連づけられたモデルに対してdestroy
が実行されるようになります。
class Hero < ApplicationRecord
has_one :weapon, dependent: :destroy
end
たとえばこのコードの場合、Heroモデルがdestroy
されたら、Weaponモデルに対してもdestroy
が実行されるわけです。
Weapon.destroy # これと同じ処理が実行される。
関連した処理も実行される
destroy
が実行されるので、それに関連した処理も逐次実行されていくことになります。
before_destroy
などdestroy
に対するフックが実行されますし、孫クラスにもdependent :destroy
があれば、孫クラスも合わせて削除されます。
それは反面、実行すべき処理が多いというデメリットでもあります。
関連が未取得の場合は最初にSELECT
が実行されます。特にhas_many
の場合は1個1個SELECT
されることがないよう注意しましょう。
pry(main)> hero.destroy (0.2ms)
BEGIN Weapon Load (0.4ms) SELECT `weapons`.* FROM `weapons` WHERE `weapons`.`id` = 1 LIMIT 1
# Weapon::before_destroyが実行されました。
SQL (0.4ms) DELETE FROM `weapons` WHERE `weapons`.`id` = 1
使うタイミング
- 削除時に、指定したモデルのレコードも削除したい。(レコード数が増えると困る)
before_destroy
や、孫クラスのdependent
など、destroy
に関連した処理も実行したい。- 速度・サーバ負荷が気にならない
dependent: delete / delete_all
delete
かdelete_all
を指定すると、削除時に指定したモデルに対してDELETEクエリが直接実行されます。
Weapon.delete # これと同じ処理が実行される。
belongs_to
, has_one
の場合は:delete
、has_many
の場合は:delete_all
と記述します。
DBからレコードを削除するという必要最低限の処理のみが行われるため、destroy
と比較して処理が早いです。
また関連した処理も実行されないため、before_destroy
などの関連した処理を実行されると困る場合などでも使えます。
使うタイミング
- 削除時に、指定したモデルのレコードも削除したい。(レコード数が増えると困る)
before_destroy
や、孫クラスのdependent
など、destroy
に関連したフックを実行したくない。- 速度・サーバ負荷が気になる。
dependent: nullify
nullify
を指定すると、削除時に指定したモデルの外部キーにnull
が入れられます。
class Hero < ApplicationRecord
has_one :weapon, dependent: :nullify
end
こちらのコードの場合、Weaponテーブルの該当レコードにおける、hero_id
がNULL
にUPDATE
されるわけですね。
削除されることはないため、残ったレコードは永遠に残り続けることになります。
そのため記録としてデータを残しておきたいときなどに便利です。
またUPDATE
のみなのでDELETE
より負荷は軽く、削除の負荷に耐えられないような高負荷サービスでも選択肢に入るでしょう。
とはいえ無尽蔵にデータが増え続けるため、いずれテーブル内の無駄なレコードを整理する必要が出てくることは想定しておく必要があります。
使うタイミング
- 削除されたレコードは残しておきたいが、参照先のない外部キーは防ぎたい。
- レコードが増え続けても問題がない、もしくは深夜バッチなど自分のタイミングで削除を行いたい。
DELETE
する負荷に耐えられない。
dependent: restrict_with_exception / restrict_with_error
削除時に関連づけられたレコードが存在するときに、例外やエラーを発生させます。
restrict_with_exception | 例外を発生させる。 ( DeleteRestrictionError がraiseする) |
restrict_with_error | エラーとなる。 ( ActiveRecord のerrorとして扱われる) |
ですのでシンプルに子要素があるときに削除されては困る場合に使うことになるでしょう。
たとえば「写真が存在するアルバムなので削除できません」とエラーメッセージを出したいときなどですね。
使うタイミング
- 関連づけられたレコードがあるときに削除されたら困る。
まとめ:ActiveRecordのdependentオプションとは
ActiveRecord
のdependentオプションについて説明していきました。
dependent
で削除時の挙動を指定するということは、dependent
を設定しないと子要素に対しては何もされないということでもあります。
削除時に何か実行したい処理がある場合は、忘れずに設定するようにしましょう!