Determine what ids were removed from array in Rails after_save callback?
I am trying to figure out how i can tell what has changed in an array in the after save callback. Here is an example of code i am using:
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy
has_many :subusers, :through => :user_maps, :dependent => :destroy
has_many :inverse_user_maps, :class_name => "UserMap", :foreign_key => "subuser_id"
has_one :parent, :through => :inverse_user_maps, :source => :user
after_save :remove_subusers
def remove_subusers
if self.subuser_ids_were != self.subuser_ids
leftover = self.subuser_ids_were - self.subuser_开发者_Go百科ids
leftover.each do |subuser|
subuser.destroy
end
end
end
end
class UserMap < ActiveRecord::Base
belongs_to :user
belongs_to :subuser, :class_name => "User"
end
I am removing the subusers with the after_save callback because i could not get the dependent destroy feature to work through user_maps. Does anyone have any ideas on a way to do this?
Thanks!
You can use the Dirty module accessors http://ar.rubyonrails.org/classes/ActiveRecord/Dirty.html as suggested in Determine what attributes were changed in Rails after_save callback?
In your case the handler you have for after_save
will have access to subusers_change
which is an array of two elements, first being the previous value and second being the new value.
although not strictly the answer to your question, I think you maybe able to get :dependent => :destroy working if you try the following...
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy
has_many :subusers, :through => :user_maps # removing the :dependent => :destroy option
end
class UserMap < ActiveRecord::Base
belongs_to :user
belongs_to :subuser, :class_name => "User", :dependent => :destroy # add it here
end
By moving the :dependent => :destroy option to the belongs_to association in the UserMap model you set up a cascading delete via the UserMap#destroy method. In other words, calling User#destroy will call UserMap#destroy for each UserMap record, which will in turn call sub_user.destroy for its sub_user record.
EDIT
Since the solution above didn't work, my next suggestion would be to add a callback to the user_maps association, however this comes with a warning that I will add after
class User < ActiveRecord::Base
has_many :user_maps, :dependent => :destroy, :before_remove => :remove_associated_subuser
def remove_associated_subuser(user_map)
user_map.subuser.destroy
end
end
WARNINGS
1) Using a before_remove callback will mean that the user_map.destroy function won't be called if there is an error with the callback
2) You will have to destroy your UserMap record using the method on the User class for example...
# this will fire the callback
u = User.first
u.user_maps.destroy(u.user_maps.first)
# this WONT fire the callback
UserMap.first.destroy
All things considered, this would make me nervous. I would first try modifying your code to make the associations a little less coupled to the same tables, so the :dependent => :destroy option can work, and if you can't do that, add a cascade delete constraint on to the database, at least then your associations will always be removed regardless of where / how you destroy it in your rails app.
精彩评论