Как сохранить модель без выполнения обратных вызовов в 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