开发者

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/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜