Reputation: 52570
I need to calculate values when saving a model in Rails. So I call calculate_averages
as a callback for a Survey
class:
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:
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_averages
is 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_averages
get 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?
Secondly, the way averages are calculated it's far more 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?
Upvotes: 39
Views: 63161
Reputation: 9491
Another cleaner method would be:
User.suppress do
f = User.last
f.save
end
Found out this in the rails documentation.
Upvotes: 0
Reputation: 1568
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.
Upvotes: 5
Reputation: 1130
Rails 5.2.3 requiring an after party script to NOT trigger model events, update_column(column_name, value) did the trick:
task.update_column(task_status, ReferenceDatum::KEY_COMPLETED)
https://apidock.com/rails/ActiveRecord/Persistence/update_column
Upvotes: 4
Reputation: 1358
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.
Upvotes: 23
Reputation: 333
Doesn't work for Rails 5
Survey.skip_callback(:save, :before, :calculate_average)
Works for Rails 5
Survey.skip_callback(:save, :before, :calculate_average, raise: false)
https://github.com/thoughtbot/factory_bot/issues/931
Upvotes: 15
Reputation: 2491
update_column
is an ActiveRecord
function which does not run any callbacks, and it also does not run validation.
Upvotes: 22
Reputation: 231
For Rails 3 ActiveSupport::Callbacks
gives you the necessary control. You can reset_callbacks
en-masse, or use skip_callback
to disable judiciously like this:
Vote.skip_callback(:save, :after, :add_points_to_user)
…after which you can operate on Vote instances with :add_points_to_user
inhibited
Upvotes: 3
Reputation: 1352
hopefully this is what you're looking for.
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.
Upvotes: 2
Reputation: 38645
I believe what you are asking for can be achieved with ActiveSupport::Callbacks
. Have a look at set_callback
and skip_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.
.
set_callback :save, :before, :my_before_save_callback
To skip the before_save
callback, you would do:
Survey.skip_callback(:save, :before, :calculate_average).
Please reference the linked ActiveSupport::Callbacks
on other supported options such as conditions and blocks to set_callback
and skip_callback
.
Upvotes: 25