问题
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.
回答1:
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.
回答2:
I faced this issue as well, and came up with a solution that isn't pretty but works. Essentially, you'd just use a destroy_user
that's similar to destroy_staff
.
class User < ActiveRecord::Base
has_one: :staff
after_destroy :destroy_staff
def destroy_staff
staff.destroy if staff && !staff.destroyed?
end
end
class Staff < ActiveRecord::Base
belongs_to :user
after_destroy :destroy_user
def destroy_user
user.destroy if user && !user.destroyed?
end
end
来源:https://stackoverflow.com/questions/23208579/is-there-a-rails-4-circular-dependent-destroy-workaround