2021-04-18
そこで
`destroyed_by_association` で親モデルの関連により削除されるかどうかを判定する
Rails (ActiveRecord) ネタ。
#destroyed_by_association
を使うと、モデル間の関連設定により削除されるのか、それとも自身で削除するのかを判定することができる。
例えば、次のように Team
と User
という2つのモデルがあるとする。
class Team < ApplicationRecord
has_many :users, dependent: :destroy
end
class User < ApplicationRecord
belongs_to :team
end
Teamが親モデルに相当し、その下に複数の子モデルUserが紐づくという単純な関係のモデル構造。
Teamモデルには has_many :users, dependent: :destroy
というdependentオプションが設定されているので、Teamを削除したときに関連づくUserは一緒に削除される。以下は、それを示すサンプルコード。
# チームを1つ作る
team = Team.create!(name: 'awesome team')
# 作成したチームの下に3ユーザを紐付ける
team.users.create!(name: 'user1')
team.users.create!(name: 'user2')
team.users.create!(name: 'user3')
team.destroy!
# TRANSACTION (0.1ms) begin transaction
# User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."team_id" = ? [["team_id", 8]]
# User Destroy (0.6ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 15]]
# User Destroy (0.2ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 16]]
# User Destroy (0.1ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 17]]
# Team Destroy (0.3ms) DELETE FROM "teams" WHERE "teams"."id" = ? [["id", 8]]
# TRANSACTION (1.6ms) commit transaction
この機能はとても便利なのだが、状況によっては、Userは上のように親モデルの関連により削除されるのか、それとも user.destroy!
のようにしてUserが自ら削除をしたのか判定したくなるときがある。
そこで #destroyed_by_association
このメソッドを使うと、Userモデル側でどちらの方法で削除されるかを判定することができる。以下、その例。
class User < ApplicationRecord
belongs_to :team
before_destroy do
if destroyed_by_association
puts 'destroyed by Team association'
else
puts 'destoryed by myself'
end
end
end
before_destroy
フックと組み合わせることで判定している。以下、この動きを示すサンプルコード。
team = Team.create!(name: 'awesome team')
team.users.create!(name: 'user1')
team.users.create!(name: 'user2')
user3 = team.users.create!(name: 'user3')
user3.destroy!
# => "destoryed by myself"
team.reload.destroy!
# => "destroyed by Team association"
# => "destroyed by Team association"
この機能をうまく使えば、User側から削除する場合だけ何らかの処理をはさみたい(あるいはその逆)の機能の実装をモデル層で吸収することができる(かもしれない)。
以上、最近知った機能の紹介でした。
※ 参考
<< 前の記事.tigrcの地道な改善
次の記事 >>Gatsbyで新しくブログを作った