As an example for the circular dependent: :destroy
issue:
class User < ActiveRecord::Base
has_one: :staff, dependent: :destroy
end
class Staff < ActiveRecord::Base
belongs_to :user, dependent: :destroy
end
If I call user.destroy
, the associated staff
should be destroyed as well. Conversely, calling staff.destroy
should destroy the associated user
as well.
This worked great in Rails 3.x, but the behavior changed in Rails 4.0 (and continues in 4.1) such that a loop forms and eventually you get an error, "stack level too deep." One obvious workaround is to create a custom callback using before_destroy
or after_destroy
to manually destroy the associated objects instead of using the dependent: :destroy
mechanism. Even the issue in GitHub opened for this situation had a couple people recommending this workaround.
Unfortunately, I can't even get that workaround to work. This is what I have:
class User < ActiveRecord::Base
has_one: :staff
after_destroy :destroy_staff
def destroy_staff
staff.destroy if staff and !staff.destroyed?
end
end
The reason this doesn't work is that staff.destroyed?
always returns false
. So it forms a cycle.
Dependent is an option of Rails collection association declaration to cascade the delete action. The :destroy is to cause the associated object to also be destroyed when its owner is destroyed.
If one side of the cycle only has that one callback, you can replace one of the dependent: :destroy
with dependent: :delete
class User < ActiveRecord::Base
# delete prevents Staff's :destroy callback from happening
has_one: :staff, dependent: :delete
has_many :other_things, dependent: :destroy
end
class Staff < ActiveRecord::Base
# use :destroy here so that other_things are properly removed
belongs_to :user, dependent: :destroy
end
Worked great for me, as long as one side doesn't need other callbacks to fire.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With