Как сохранить модель без выполнения обратных вызовов в Rails

Мне нужно вычислить значения при сохранении модели в Rails. Поэтому я вызываю calculate_averages как обратный вызов для класса Survey:

before_save :calculate_averages

Однако иногда (и изначально у меня есть 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

Прежде чем запускать этот код, я волнуюсь, что calculate_averages будет вызван и дублируется, и, возможно, даже вызовет некоторые проблемы с тем, как я делаю. Хорошо, тогда я думаю, хорошо, я просто ничего не сделаю и позволю calculate_averages получить вызов и сделать свое дело. Проблема в том, что есть, во-первых, способ заставить callbacks получать вызов, даже если вы не внесли никаких изменений в запись?

Во-вторых, способ вычисления средних значений намного более эффективен, чтобы просто не позволять обратным вызовам вообще вызываться и делать средние значения для всего сразу. Возможно ли, чтобы вызываемые вызовы не вызывались?

Ответ 1

Я считаю, что то, о чем вы просите, может быть достигнуто с помощью ActiveSupport::Callbacks. Посмотрите set_callback и skip_callback.

Чтобы "заставить обратные вызовы вызываться, даже если вы не вносили изменений в запись", вам необходимо зарегистрировать обратный вызов для какого-либо события, например. save, validate etc..

set_callback :save, :before, :my_before_save_callback

Чтобы пропустить обратный вызов before_save, вы должны:

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

Просьба ссылаться на связанный ActiveSupport::Callbacks на другие поддерживаемые параметры, такие как условия и блоки, на set_callback и skip_callback.

Ответ 2

Чтобы отключить массовые обратные вызовы, используйте...

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

Затем, чтобы включить их...

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

Это пропускает/устанавливает для всех экземпляров.

Ответ 3

update_column - это функция ActiveRecord, которая не выполняет никаких обратных вызовов, а также не выполняет проверку.

Ответ 4

Если вы хотите условно пропустить обратные вызовы после проверки каждого опроса, вы можете написать свой собственный метод.

Например,

Модифицированный обратный вызов

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

Новый метод экземпляра

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

Скрипт для обновления опросов

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

Это своего рода взломать, но надежда будет работать для вас.

Ответ 5

Не работает для Rails 5

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

Работает на Rails 5

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

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

Ответ 6

надеюсь, это то, что вы ищете.

fooobar.com/questions/50615/...

Для вашей второй проблемы, я подозреваю, что было бы лучше проверить, когда это вычисление должно произойти, было бы лучше, если бы ее можно было обрабатывать в пакетном режиме в указанное время, когда сетевой трафик находится в его лотке.

EDIT: Woops. Я действительно нашел 2 ссылки, но, по-видимому, проиграл первый. Надеюсь, вы его исправили.

Ответ 7

Для Rails 3 ActiveSupport::Callbacks вы получаете необходимый контроль. Вы можете reset_callbacks en-masse или использовать skip_callback, чтобы отключить разумно, как это:

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

& hellip, после чего вы можете работать с экземплярами Vote с :add_points_to_user запрещенным

Ответ 8

Rails 5.2.3, требующий, чтобы сторонний скрипт НЕ вызывал события модели, update_column (column_name, value) добился цели:

task.update_column(task_status, ReferenceDatum::KEY_COMPLETED)

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