有效地迭代和改变关联

我们的代码有效地做到了这一点。

obj.things.each |thing| ... do some stuff ... obj.things.destroy(thing) ... do some more stuff... end 

我们发现,在更改CollectionProxy的同时迭代CollectionProxy会导致只有一半的项被迭代。 目前我们正在通过将代理扁平化为数组来解决这个问题。 但这意味着将所有things复制到内存中。

 obj.things.to_a.each |thing| ... end 

有没有办法迭代和变异集合而不将整个关联拉入内存?

或者,是否有比我们使用的模式更好的模式? 例如,包装代码是我们每次销毁关联时都不想做的事情,因此我们不使用关联钩子。 我们可以写一个可以使用钩子的子类或范围吗?

更新 : 我发布了更大的问题 。

破坏外观中的对象实际上是一种不好的做法。

我可以想到两种方法,你可以在数组中保存要销​​毁的对象的id,或者在要删除的数据库上标记它们(添加一个新的update_column :to_destroy, true认为false并执行update_column :to_destroy, true )。

这样你就可以在循环之后执行Thing.where(id:ids_to_destroy).destroy_all(或类似Thing.where(to_destroy:true).destroy_all,如果你标记它们)。

使用什么方法取决于您的需求。 因为它需要更少的更改,我会将ID保存在数组中,但是也许你处理了大量的数据并且在内存中保存了一个非常大的数组太多了(不是常见的情况,因为你只是存储id,但它有可能)。

你原来的each都已经将整个关联拉入内存。

调用to_a来生成数组的额外副本(然后没有变异)确实是一种非常合理的方法。 并不是特别昂贵:数组是重复的,但实际的对象不是。

您也可以使用ActiveRecord::Base#destroy代替:

 obj.things.each |thing| ... do some stuff ... thing.destroy ... do some more stuff... end 

由于obj.things集合不再意识到破坏,它仍将包含完整的东西,因此迭代将不受影响。 (如果任何一个stuff块使用obj.things的当前内容,这显然会有问题。)