Why does ActiveRecord has_many use delete_all instead of destroy_all?
I have a model 开发者_StackOverflowwhich has many children. I was setting/removing the children as such:
mymodel.children_ids = [1,2,3]
mymodel.save #add the children
mymodel.children_ids = [1]
mymodel.save #remove children 2,3
This works just fine, but I just realized that none of the callbacks (i.e. after_destroy
) are not being called on the children model.
After some digging, it turns out that the delete_all
function is being executed, rather than destroy_all
. As the docs correctly state, the delete_all
function does not fire off the callbacks, so is there anyway to change this behavior?
Thanks.
Something like:
mymodel.children.find([2,3]).each {|c| c.destroy }
will do the job. It's not exactly what you wanted, but I hope it helps.
delete_all
is being executed... just because? That's what the Rails core team thought should be called in that instance. However, you could explicitly call the destroy_all
method on the model's children, but not using that type of query. It looks like you're setting the child IDs directly, not using any build()
or destroy()
methods.
Yes, you can overwrite its functionality. You could patch it and put it in /vendor
, rewriting the same block of code with destroy_all
instead. Then use a send
command to replace the base ActiveRecord functionality with your own.
For those interested, I added the following monkeypatch to force the has_many through to perform a destroy_all, rather than delete_all. There might be a better way, so I'm open to suggestions.
module ActiveRecord
module Associations
class HasManyThroughAssociation < HasManyAssociation
def delete_records(records)
klass = @reflection.through_reflection.klass
records.each do |associate|
klass.destroy_all(construct_join_attributes(associate)) #force auditing by using destroy_all rather than delete all
end
end
end
end
end
I had similar problem with callbacks. Solved it using alias_method_chain to override default setter.
def product_categories_with_destroy=(product_categories)
unless new_record?
(self.product_categories - product_categories).each(&:destroy)
end
self.product_categories_without_destroy = product_categories
end
alias_method_chain :product_categories=, :destroy
More details on alias_method_chain:
http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/
精彩评论