16

I've got a record in my Rails app with an after_destroy hook that needs to be aware of why the record gets destroyed. More specifically, if the record is being destroyed in a cascade because its parent says dependent: :destroy, it needs to do things differently than if the record was individually destroyed.

What I tried to do is to see if its parent was destroyed?, only to figure out that dependent: :destroy callbacks are done before the parent is destroyed. Which makes sense because it should be able to fail. (i.e. restrict).

So, how do I do this?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Pelle
  • 6,423
  • 4
  • 33
  • 50
  • @eabraham As I wrote in my question: What I tried to do is to see if its parent was `destroyed?`, only to figure out that `dependent: :destroy` callbacks are done before the parent is destroyed. In other words: that is useless, the paren't can't be destroyed as the dependent callback is issued before the parent gets destroyed. – Pelle Feb 05 '14 at 14:29

2 Answers2

12

Solution #1

If your model is simple enough and you don't need to invoke any callbacks in the child relation, you can just use dependent: delete_all in the parent.

Solution #2

For more complex scenarios you can use destroyed_by_association, which returns a ActiveRecord::Reflection::HasManyReflection object when it's part of cascade, or nil otherwise:

after_destroy :your_callback

def your_callback
  if destroyed_by_association
    # this is part of a cascade
  else
    # isolated deletion
  end
end

I just tried this in Rails 4.2 and it works.

Source: https://github.com/rails/rails/issues/12828#issuecomment-28142658

alf
  • 18,372
  • 10
  • 61
  • 92
2

One way to do this is using the before_destroy callback in the parent object to mark all child objects as destroyed through parent destroy. Like this:

class YourClass
  before_destroy :mark_children

  ...
  ...

  def mark_children
    [:association1, :association2].each do |association|   # Array should include association names that hate :dependent => :destroy option
      self.send(association).each do |child|
        # mark child object as deleted by parent
      end
    end
  end
end

You can also use ActiveRecord reflections to determine automatically which associations are marked as :dependent => :destroy. Doing this is helpful when you need this function in many classes.

Pere Joan Martorell
  • 2,608
  • 30
  • 29
davidb
  • 8,884
  • 4
  • 36
  • 72