我遇到了意想不到的行为。当我在一个关系上调用destroy_all
时,它通过一个ActiveRecord事务执行,但是当我在它之前放置一个where子句时,有一个意外的行为,每个记录都被单独销毁。
的例子:
Actor.find(1).movies.destroy_all
这里destroy_all
将在事务中运行,但是Actor.find(1).movies.where(id: [1,2,3]).destroy_all
将单独执行每个销毁。
有解释吗?
这是因为你在处理不同的对象:
Actor.find(1).movies.class
# Movie::ActiveRecord_Associations_CollectionProxy
Actor.find(1).movies.where(id: [1,2,3]).class
# Movie::ActiveRecord_AssociationRelation
两个类都以自己的方式定义了自己的delete_all
方法:
# File activerecord/lib/active_record/associations/collection_proxy.rb
def destroy_all
@association.destroy_all.tap { reset_scope }
end
# File activerecord/lib/active_record/relation.rb
def destroy_all
records.each(&:destroy).tap { reset }
end
所以当你执行Actor.find(1).movies.destroy_all
时,动作是通过在@association
上调用destroy_all
来处理的。
但是通过执行Actor.find(1).movies.where(id: [1,2,3]).destroy_all
,每个对象都有一个迭代,对每个对象调用destroy
。
@association.delete_all
被定义为获取一个ActiveRecord_Relation
,并在单个事务中执行销毁包装它们的元素:
def destroy_all
destroy(load_target).tap do
reset
loaded!
end
end
通过尝试该方法,您可以得到与第一个示例相同的结果;
Actor.find(1).movies.instance_variable_get("@association").send(:destroy, Movie.all)