Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails polymorphic dependent: :destroy not working correctly

Let's say I have three Active Record models:

class Tissue
  has_many :boogers, as: :boogerable, dependent: :destroy
end

class Finger
  has_many :boogers, as: :boogerable, dependent: :destroy
end

Class Boogers
  belongs_to :boogerable, polymorphic: true
end

Let's say my application can transfer a booger from a finger to a tissue, and once that's done, the finger is destroyed (yikes!). For some reason, when the finger is destroyed, it is also destroying the boogers that previously belonged to it and were since transferred to a tissue. In the log I can see that when the finger is destroyed it remembers the ID of boogers that USED to belong to it and destroys them. It doesn't check to make sure the boogerable_type is still 'finger' and thus destroys the ones with type 'tissue'.

When a finger is destroyed, it is doing this:

DELETE FROM boogers WHERE booger.id = 387

When it should be doing this:

DELETE FROM boogers WHERE boogerable_id = 1 AND boogerable_type = 'finger'

Anyone come across this before?

like image 999
Brent Eicher Avatar asked Jun 11 '14 23:06

Brent Eicher


1 Answers

First, you need to know that the ActiveRecord destroy method uses the object's :id field when it needs to destroy it. And that the dependent: :destroy option will trigger the destroy method upon all objects that are assciated with the current object.

I assume that you're doing something like this:

f = Finger.find(finger_id)
t = Tissue.find(tissue_id)

f.boogers[0].boogerable = t
f.boogers[0].save!

f.destroy

The dependent: :destroy statement means that when destroy is called upon f, it will also be called upon all boogers that are associated with f. And since f and its boogers are loaded into memory, the f.booger[0] object still exists in the array of f.boogers which will have each of its elements, including boogers[0], destroyed when f is destroyed.

The solution of this case is that you hit f.boogers.reload to update the array before calling f.destroy.

Also, please note that when you destroy f, all associated boogers will be destroyed as well, is that really what you want? destroy the finger with all remaining associated boogers when one of them is transferred to something else?

like image 70
Tamer Shlash Avatar answered Sep 16 '22 19:09

Tamer Shlash