【问题标题】:Efficiently iterate and mutate an association有效地迭代和改变关联
【发布时间】:2018-09-19 18:24:23
【问题描述】:

我们有代码可以有效地做到这一点。

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

有没有办法在不将整个关联拉入内存的情况下对集合进行迭代和变异?

或者,有没有比我们使用的更好的模式?例如,包装代码是我们不想在每次销毁关联时都做的事情,所以我们没有使用关联挂钩。我们可以编写一个可以使用钩子的子类或作用域吗?

更新I've posted about the larger issue

【问题讨论】:

    标签: ruby-on-rails collections ruby-on-rails-5


    【解决方案1】:

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

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

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

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

    【讨论】:

    • 如果您在迭代时将 id 保存到数组中,那么您甚至不需要查找它们,您可以使用 obj.things.destroy(*ids_to_delete_array),因为 CollectionProxy#destroy 可以接受多个参数
    • 如果你将 ids 传递给'destroy'它会做同样的事情。记录需要被实例化,因为 ActiveRecord 对每个记录都调用“销毁”。我个人更喜欢 'where(...).destroy_all'。
    • I've posted about the larger issue which lead to this。如果您能看一看,我将不胜感激。
    【解决方案2】:

    您原来的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 的当前内容,这显然是有问题的。)

    【讨论】:

    • 很高兴知道只有数组是重复的,谢谢。实际情况有点复杂,因为each 循环实际上是在调用一个做更多工作的方法,然后调用obj.things.destroy 以确保集合保持最新。
    • I've posted about the larger issue which lead to this。如果您能看一看,我将不胜感激。
    • @Schwern 您可以确保集合在每个块之后调用obj.reload 保持最新。正如 arieljuod 在另一个答案中所说,从集合循环块内的集合中删除对象是一种不好的做法。
    • @DanielBatalla 是的,我更喜欢使用destroy_all
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 2014-01-19
    • 1970-01-01
    • 2017-09-08
    • 2019-06-14
    • 2018-12-25
    • 1970-01-01
    • 2018-08-15
    相关资源
    最近更新 更多