Ruby-on-rails 如何在不运行 Rails 回调的情况下保存模型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21371734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:17:19  来源:igfitidea点击:

How to save a model without running callbacks in Rails

ruby-on-railsruby-on-rails-3activerecordruby-on-rails-3.2rails-activerecord

提问by at.

I need to calculate values when saving a model in Rails. So I call calculate_averagesas a callback for a Surveyclass:

在 Rails 中保存模型时,我需要计算值。所以我调用calculate_averages一个Survey类的回调:

before_save :calculate_averages

However, occasionally (and initially I have 10k records that need this operation) I need to manually update all the averages for every record. No problem, I have code like the following:

但是,偶尔(最初我有 10k 条记录需要此操作)我需要手动更新每条记录的所有平均值。没问题,我有如下代码:

Survey.all.each do |survey|
  survey.some_average = (survey.some_value + survey.some_other_value) / 2.to_f
  #and some more averages...
  survey.save!
end

Before even running this code, I'm worried the calculate_averagesis going to get called and duplicate this and probably even cause some problems with the way I'm doing things. Ok, so then I think, well I'll just do nothing and let calculate_averagesget called and do its thing. Problem there is, first, is there a way to force callbacks to get called even if you made no changes to the record?

在运行这段代码之前,我担心calculate_averages它会被调用并复制它,甚至可能会导致我做事的方式出现一些问题。好吧,那么我想,好吧,我什么都不做,让我们calculate_averages打电话去做它的事情。问题是,首先,即使您没有对记录进行任何更改,有没有办法强制调用回调?

Secondly, the way averages are calculated it's farmore efficient to simply not let the callbacks get called at all and do the averages for everything all at once. Is this possible to not let callbacks get called?

其次,计算平均值的方式是,根本不让回调被调用并一次性计算所有内容的平均值要高效得多。这有可能不让回调被调用吗?

回答by vee

I believe what you are asking for can be achieved with ActiveSupport::Callbacks. Have a look at set_callbackand skip_callback.

我相信你所要求的可以通过ActiveSupport::Callbacks. 看看set_callbackskip_callback

In order to "force callbacks to get called even if you made no changes to the record", you need to register the callback to some event e.g. save, validate etc..

为了“即使您没有对记录进行任何更改,也强制调用回调”,您需要将回调注册到某个事件,例如save, validate etc..

set_callback :save, :before, :my_before_save_callback

To skip the before_savecallback, you would do:

要跳过before_save回调,您可以执行以下操作:

Survey.skip_callback(:save, :before, :calculate_average). 

Please reference the linked ActiveSupport::Callbackson other supported options such as conditions and blocks to set_callbackand skip_callback.

请参考ActiveSupport::Callbacks其他支持的选项(如条件和块)的链接set_callbackskip_callback

回答by rafroehlich2

To disable en-mass callbacks use...

要禁用批量回调,请使用...

Survey.skip_callback(:save, :before, :calculate_averages)

Then to enable them...

然后启用它们...

Survey.set_callback(:save, :before, :calculate_average)

This skips/sets for all instances.

这将跳过/设置所有实例。

回答by Ahmad Hussain

update_columnis an ActiveRecordfunction which does not run any callbacks, and it also does not run validation.

update_column是一个ActiveRecord不运行任何回调的函数,它也不运行验证。

回答by dan987

Doesn't work for Rails 5

不适用于 Rails 5

Survey.skip_callback(:save, :before, :calculate_average) 

Works for Rails 5

适用于 Rails 5

Survey.skip_callback(:save, :before, :calculate_average, raise: false)

https://github.com/thoughtbot/factory_bot/issues/931

https://github.com/thoughtbot/factory_bot/issues/931

回答by Swaps

If you want to conditionally skip callbacks after checking for each survey you can write your custom method.

如果您想在检查每个调查后有条件地跳过回调,您可以编写自定义方法。

For ex.

例如。

Modified callback

修改回调

before_save :calculate_averages, if: Proc.new{ |survey| !survey.skip_callback }

New instance method

新实例方法

def skip_callback(value = false)
  @skip_callback = @skip_callback ? @skip_callback : value
end

Script to update surveys

用于更新调查的脚本

Survey.all.each do |survey|
  survey.some_average = (survey.some_value + survey.some_other_value) / 2.to_f
  #and some more averages...
  survey.skip_callback(true)
  survey.save!
end

Its kinda hack but hope will work for you.

它有点黑客,但希望对你有用。

回答by FlimFlam Vir

Rails 5.2.3 requiring an after party script to NOT trigger model events, update_column(column_name, value) did the trick:

Rails 5.2.3 需要一个 after party 脚本来不触发模型事件, update_column(column_name, value) 做到了:

task.update_column(task_status, ReferenceDatum::KEY_COMPLETED)

https://apidock.com/rails/ActiveRecord/Persistence/update_column

https://apidock.com/rails/ActiveRecord/Persistence/update_column

回答by Rafeeq

For Rails 3 ActiveSupport::Callbacksgives you the necessary control. You can reset_callbacksen-masse, or use skip_callbackto disable judiciously like this:

对于 Rails 3 ActiveSupport::Callbacks,您可以进行必要的控制。您可以像这样明智地禁用reset_callbacks或使用集体skip_callback禁用:

Vote.skip_callback(:save, :after, :add_points_to_user)

…after which you can operate on Vote instances with :add_points_to_userinhibited

......之后,您可以与投票实例操作:add_points_to_user抑制

回答by Sherwyn Goh

hopefully this is what you're looking for.

希望这就是你要找的。

https://stackoverflow.com/a/6587546/2238259

https://stackoverflow.com/a/6587546/2238259

For your second issue, I suspect it would be better to inspect when this calculation needs to happen, it would be best if it could be handled in batch at a specified time where network traffic is at its trough.

对于您的第二个问题,我怀疑最好检查何时需要进行此计算,最好是在网络流量处于低谷的指定时间批量处理。

EDIT: Woops. I actually found 2 links but lost the first one, apparently. Hopefully you have it fixed.

编辑:哎呀。我实际上找到了 2 个链接,但显然丢失了第一个链接。希望你把它修好了。