如何在需要回调时更新所有内容?

And*_*rew 42 activerecord ruby-on-rails callback ruby-on-rails-3 update-all

假设我在一个名为的数组中有15个用户id user_ids.

如果我想,比如说,将他们所有的名字改为"Bob",我可以这样做:

users = User.find(user_ids)
users.update_all( :name => 'Bob' )
Run Code Online (Sandbox Code Playgroud)

但是,这不会触发回调.如果我需要触发对这些记录保存的回调,据我所知,唯一的方法是使用:

users = User.find(user_ids)
users.each do |u|
  u.name = 'Bob'
  u.save
end
Run Code Online (Sandbox Code Playgroud)

但是,这可能意味着在控制器操作中执行非常长时间的任务.

所以,我的问题是,是否还有其他更好/更高性能/更多的方式来触发批量更新到一组记录,这些记录触发记录的回调?

unt*_*led 40

而不是使用每个/ find_each,update而是尝试使用方法:

models.update(column: value)
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于Rails 5. (13认同)
  • 截至目前,这应该是答案. (6认同)
  • @Quv 不,这不应该。在这种情况下,“Relation#update”只是“each + update”的包装。@iafonov 的解释仍然站得住脚。 (2认同)

iaf*_*nov 22

不,要运行回调,您必须实例化一个昂贵的操作对象.我认为解决问题的唯一方法是将您在回调中执行的操作重构为单独的方法,该方法可以使用select_all方法检索的数据而无需对象实例化.

  • 这个答案已经过时,不适用于 Rails 5+。请参阅[@untitled的答案](/sf/answers/3153950131/)。 (2认同)

max*_*m10 19

这是触发回调的另一种方式.而不是使用

models.update_all(params)
Run Code Online (Sandbox Code Playgroud)

您可以使用

models.find_each { |m| m.update_attributes(params) }
Run Code Online (Sandbox Code Playgroud)

但是,如果您处理的是非常大量的数据,我不会推荐这种方法.
希望能帮助到你!

  • 我的观点是`map`*将*调用回调(以实例化模型为代价),而`update_all`不会调用它们.`find_each`的优点是它可以批量处理你的模型(一次默认1000个),而不是同时将所有模型加载到内存中(如果你有数千个模型,这可能会给你带来巨大的性能影响) .`models.find_each {| M | .update_attributes(PARAMS)}`将具有`models.each相同的效果{| M | .update_attributes(PARAMS)}`或`models.map {| M | .update_attributes(PARAMS) }` (7认同)
  • 这与`update_all`并不是一回事.`map`将为每个模型实例化一个Ruby对象,并为每个模型发出单独的SQL查询.`update_all`会将所有内容捆绑到一个SQL查询中,并且不会实例化对象 - 这就是为什么它对大型集合来说要快得多.但是就像@ iafonov提到的答案一样,你无法同时获得`update_all`和回调的速度,因为根据定义你不能在没有实例化模型的情况下调用回调. (3认同)