ruby-on-rails - que - ruby on rails tutorial
Cómo guardar un modelo sin ejecutar devoluciones de llamada en Rails (7)
Necesito calcular valores al guardar un modelo en Rails. Así que llamo a calculate_averages
como una devolución de llamada para una clase de Survey
:
before_save :calculate_averages
Sin embargo, ocasionalmente (e inicialmente tengo 10k registros que necesitan esta operación) necesito actualizar manualmente todos los promedios para cada registro. No hay problema, tengo un código como el siguiente:
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
Incluso antes de ejecutar este código, me preocupa que los calculate_averages
se llamen y se dupliquen, y probablemente incluso causen algunos problemas con la forma en que hago las cosas. Bien, entonces creo que, bueno, no haré nada y dejaré que calculate_averages
y hagamos su trabajo. El problema es que, primero, ¿hay una manera de forzar la devolución de llamadas, incluso si usted no hizo cambios en el registro?
En segundo lugar, la forma en que se calculan los promedios es mucho más eficiente simplemente porque no permite que se realicen llamadas en absoluto y que se hagan los promedios de todo al mismo tiempo. ¿Es esto posible no permitir que se devuelvan llamadas?
Creo que lo que está pidiendo se puede lograr con ActiveSupport::Callbacks
. Echa un vistazo a set_callback
y skip_callback
.
Para "forzar que se devuelvan las devoluciones de llamada, incluso si no ha realizado cambios en el registro", debe registrar la devolución de llamada en algún evento, por ejemplo save, validate etc.
set_callback :save, :before, :my_before_save_callback
Para omitir la before_save
llamada before_save
, harías:
Survey.skip_callback(:save, :before, :calculate_average).
Consulte el ActiveSupport::Callbacks
en otras opciones compatibles, como las condiciones y los bloques, a set_callback
y skip_callback
.
Esperemos que esto es lo que estás buscando.
https://.com/a/6587546/2238259
Para su segundo problema, sospecho que sería mejor inspeccionar cuándo es necesario que ocurra este cálculo, sería mejor si pudiera manejarse por lotes en un momento específico donde el tráfico de red está en su punto más bajo.
EDIT: Woops. De hecho, encontré 2 enlaces pero perdí el primero, al parecer. Esperemos que lo tengas arreglado.
No funciona para Rails 5
Survey.skip_callback(:save, :before, :calculate_average)
Trabajos para rieles 5
Survey.skip_callback(:save, :before, :calculate_average, raise: false)
Para Rails 3 ActiveSupport::Callbacks
te da el control necesario. Puede reset_callbacks
en masa o usar skip_callback
para deshabilitar juiciosamente de esta manera:
Vote.skip_callback(:save, :after, :add_points_to_user)
… Después de lo cual puede operar en instancias de Vote con :add_points_to_user
inhibited
Para deshabilitar las devoluciones de llamada en masa use ...
Survey.skip_callback(:calculate_averages)
Entonces para habilitarlos ...
Survey.set_callback(:calculate_average)
Esto salta / establece para todas las instancias.
Si desea omitir devoluciones de llamada condicionalmente después de verificar cada encuesta, puede escribir su método personalizado.
Por ej.
- Callback modificado
`
before_save :calculate_averages, if: Proc.new{ |survey| !survey.skip_callback }
`
- Nuevo método de instancia
`
def skip_callback(value = false)
@skip_callback = @skip_callback ? @skip_callback : value
end
`
- Script para actualizar encuestas-
`
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
`
Es un poco hack pero la esperanza funcionará para ti.
update_column
es una función ActiveRecord
que no ejecuta ninguna devolución de llamada y tampoco ejecuta la validación.