🐥

ActiveRecord の around_destroy と dependent: :destroy の呼び出し順

2024/09/25に公開

あんまりないと思うんですが Model で設定した around_destroydependent: :destroy で削除されるレコードに依存している場合に呼び出し順に注意する必要があります。
例えば以下のように dependent: :destroy -> around_destroy の順で定義した場合に意図する動きになりません。

class User < ActiveRecord::Base
  has_many :comments, dependent: :destroy

  around_destroy :around_destroy
  def around_destroy
    # 先に値を取得しておいて
    comments = Comment.where(user_id: id).pluck(:text)
    yield
    # おわったら出力する
    # が、これだとうまく動かない
    pp comments
    # => []
  end
end


user = User.create(name: "homu")

user.comments.create(text: "はい")
user.comments.create(text: "Yes")
user.comments.create(text: "わかりました")

# このタイミングで user に紐づく comments が出力してほしい
user.destroy!

これは around_destroy よりも先に dependent: :destroy でレコードが削除されてしまっているからです。
こういう場合は先に around_destroy を定義することで回避することができます。

class User < ActiveRecord::Base
  around_destroy :around_destroy
  def around_destroy
    comments = Comment.where(user_id: id).pluck(:text)
    yield
    pp comments
    # => ["はい", "Yes", "わかりました"]
  end

  # around_destroy よりも後で has_many を定義する
  has_many :comments, dependent: :destroy
end

user = User.create(name: "homu")

user.comments.create(text: "はい")
user.comments.create(text: "Yes")
user.comments.create(text: "わかりました")

user.destroy!

コールバックでは呼び出し順に注意しましょう。

GitHubで編集を提案

Discussion