web-dev-qa-db-ja.com

Railsでコールバックを実行せずにモデルを保存する方法

Railsでモデルを保存するときに値を計算する必要があります。だから私はcalculate_averagesSurveyクラスのコールバックとして:

before_save :calculate_averages

ただし、ときどき(この操作を必要とする1万件のレコードがある場合)、すべてのレコードのすべての平均を手動で更新する必要があります。問題ありません。次のようなコードがあります。

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呼び出され、その処理を行います。問題は、まず、レコードに変更を加えなかった場合でも、コールバックを強制的に呼び出す方法がありますか?

第二に、平均値の計算方法farは、コールバックがまったく呼び出されないようにして、すべての平均値を一度に実行する方が効率的です。コールバックが呼び出されないようにすることは可能ですか?

24
at.

あなたが求めていることは ActiveSupport::Callbacks で達成できると信じています。 set_callbackskip_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 を参照してください。

23
vee

一括コールバックを無効にするには...

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

その後、それらを有効にするには...

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

これにより、すべてのインスタンスがスキップ/設定されます。

15
rafroehlich2

update_columnは、コールバックを実行しないActiveRecord関数であり、検証も実行しません。

10
Ahmad Hussain

各調査を確認した後に条件付きでコールバックをスキップする場合は、カスタムメソッドを記述できます。

例えば.

  • 変更されたコールバック-

`

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

`

ちょっとしたハックですが、希望はあなたのために働くでしょう。

4
Swaps

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

4
dan987

うまくいけば、これがあなたが探しているものです。

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

2番目の問題については、この計算がいつ必要になるかを検査する方が良いと思われます。ネットワークトラフィックがトラフにある指定された時間にバッチで処理できれば最高です。

編集:うわー。実際に2つのリンクを見つけましたが、最初のリンクは失われたようです。うまくいけば、それが修正されます。

2
Sherwyn Goh

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

1
FlimFlam Vir