destroy_all 当我们在它前面放置一个 where 子句时,在事务中不起作用
destroy_all not working in transaction when we place a where clause before it
我遇到了意外行为。当我在一个关系上调用 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)
我遇到了意外行为。当我在一个关系上调用 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)