Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete Orphaned Parent

I have a relationship like so:

Parent
  has_many :children

Child
  belongs_to :parent

What I want to do is to delete the parent if there are no more children left. So to do this I have:

Child
    before_destroy :destroy_orphaned_parent

    def destroy_orphaned_parent
      parent.children.each do |c|
        return if c != self
      end
      parent.destroy
    end

This works fine, however I also want to cascade the delete of the parent to the child. E.g. I would normally do:

Parent
  has_many :children, :dependent => :destroy

This causes the WebRick server to crash when I test it. I assume this is due to an infinite loop of the last child deleting the parent deleting the child etc.

I am starting to think that there is a better way to do this? Anyone have any ideas? Is there a way to prevent this recursion?

like image 306
ghempton Avatar asked May 03 '11 07:05

ghempton


4 Answers

I accomplished this in the following way:

  before_destroy :find_parent
  after_destroy :destroy_orphaned_parent

  def find_parent
    @parent = self.parent
  end

  def destroy_orphaned_parent
    if @parent.children.length == 0
      @parent.destroy
    end  
  end

As per Anwar's suggestion, this can also be accomplished using an around callback, as follows:

  around_destroy :destroy_orphaned_parent

  def destroy_orphaned_parent
    parent = self.parent
    yield # executes a DELETE database statement
    if parent.children.length == 0
      parent.destroy
    end  
  end

I haven't tested the above solution, so feel free to update it, if necessary.

like image 59
Abram Avatar answered Oct 30 '22 20:10

Abram


Some Ideas:

  • You could delete orphaned parents in an after_destroy (find them using a statement like the one on http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/a3f12d578f5a2619)

  • You could set some instance variable in before_destroy containing the parent's ID, then do a lookup based on this id in an after_destroy callback and decide whether to delete the parent there based on counting the children

like image 30
Tapio Saarinen Avatar answered Oct 30 '22 20:10

Tapio Saarinen


Use an after_destroy callback.

after_destroy :release_parent

def release_parent
  if parent.children.count.zero?
    parent.destroy
  end
end

Using Rails 3.2.15

like image 26
scarver2 Avatar answered Oct 30 '22 21:10

scarver2


You can accomplish this using around_destroy callback

around_destroy :destroy_orphaned_parent

def destroy_orphaned_parent
  @parent = self.parent
  yield
  @parent.destroy if @parent.children.length == 0
end
like image 42
Sagar Ranglani Avatar answered Oct 30 '22 21:10

Sagar Ranglani