Usando ActiveRecord, ¿hay alguna manera de obtener los valores antiguos de un registro durante after_update

StackOverflow https://stackoverflow.com/questions/607069

Pregunta

Configuración mediante un ejemplo simple: Tengo 1 tabla ( Totales ) que contiene la suma de la columna monto de cada registro en una segunda tabla ( Things ).

Cuando se actualiza un thing.amount , me gustaría simplemente agregar la diferencia entre el valor anterior y el nuevo valor a total.sum .

Ahora mismo estoy restando self.amount durante before_update y agregando self.amount durante after_update . Esto coloca a WAY demasiada confianza en la actualización exitosa.

Restricción: No quiero simplemente recalcular la suma de todas las transacciones.

Pregunta: Sencillamente, me gustaría acceder al valor original durante una devolución de llamada after_update . ¿En qué formas se te ha ocurrido hacer esto?

Actualización: voy con la idea de Luke Francl. Durante una devolución de llamada after_update , todavía tienes acceso a los valores de self.attr_was que es exactamente lo que quería. También decidí ir con una implementación de after_update porque quiero mantener este tipo de lógica en el modelo. De esta manera, no importa cómo decido actualizar las transacciones en el futuro, sabré que estoy actualizando la suma de las transacciones correctamente. Gracias a todos por sus sugerencias de implementación.

¿Fue útil?

Solución

Ditto lo que todos dicen acerca de las transacciones.

Dicho esto ...

ActiveRecord a partir de Rails 2.1 realiza un seguimiento de los valores de los atributos de un objeto. Entonces, si tiene un atributo total , tendrá un total_changed? y un total_was que devuelve el valor anterior.

No es necesario agregar nada a tu modelo para seguir de cerca esto.

Actualización: Aquí está la documentación para ActiveModel :: Dirty según lo solicitado.

Otros consejos

Algunas otras personas están mencionando envolver todo esto en una transacción, pero creo que ya está hecho para ti; solo necesita activar la reversión al generar una excepción para los errores en las siguientes_ * devoluciones de llamada.

Consulte http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

  

La cadena de devolución de llamada completa de guardar, guardar o destruir llamadas se ejecuta dentro de una transacción. Eso incluye after_ * hooks. Si todo va bien, se ejecuta un COMPROMISO una vez que se ha completado la cadena.

     

Si una devolución de llamada before_ * cancela la acción, se emite un ROLLBACK. También puede desencadenar un ROLLBACK provocando una excepción en cualquiera de las devoluciones de llamada, incluidos los ganchos after_ *. Sin embargo, tenga en cuenta que, en ese caso, el cliente debe ser consciente de ello, ya que una operación de guardado ordinaria generará dicha excepción en lugar de devolverla falsamente.

Anexando " _was " a su atributo le dará el valor anterior antes de guardar los datos.

Estos métodos se denominan métodos sucios .

¡Salud!

Para obtener todos los campos modificados, con sus valores antiguos y nuevos respectivamente:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

ActiveRecord :: Dirty es un módulo integrado en ActiveRecord para el seguimiento cambios de atributos Por lo tanto, puede utilizar thing.amount_was para obtener el valor anterior.

Añade esto a tu modelo:

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

Luego use @old_amount en su código after_update.

Primero, debes hacer esto en una transacción para asegurarte de que tus datos se escriban juntos.

Para responder a tu pregunta, puedes establecer una variable miembro con el valor anterior en el before_update, al cual puedes acceder en el after_update, sin embargo, esta no es una solución muy elegante.

Idea 1: Envuelva la actualización en una transacción de base de datos, de modo que si la actualización falla, la tabla de Totales no se modifica: Documentos de transacciones de ActiveRecord

Idea 2: Guarda el valor antiguo en @old_total durante la fecha_actualización anterior.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top