2009-03-03 6 views
60

de instalación mediante un ejemplo sencillo: Tengo 1 mesa (Totals) que mantiene la suma de la columna de amount de cada registro de una segunda tabla (Things).El uso de ActiveRecord, ¿hay una manera de conseguir los viejos valores de un registro durante after_update

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

En este momento estoy restando self.amount durante before_update y añadiendo self.amount durante after_update. Esto pone MUCHA confianza en la actualización exitosa.

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

Pregunta: En pocas palabras, me gustaría acceder al valor original durante una devolución de llamada after_update. ¿De qué manera se te ocurrió esto?

Actualización: Voy con la idea de Luke Francl. Durante una devolución de llamada after_update, usted todavía tiene acceso a los valores self.attr_was, que es exactamente lo que yo quería. También decidí ir con una implementación after_update porque quiero mantener este tipo de lógica en el modelo. De esta forma, sin importar cómo decida actualizar las transacciones en el futuro, sabré que actualizo la suma de las transacciones correctamente. Gracias a todos por sus sugerencias de implementación.

Respuesta

124

Lo mismo que todo el mundo está diciendo sobre las transacciones.

Eso dijo ...

ActiveRecord como rieles de 2.1 comprueba los valores de los atributos de un objeto. Por lo tanto, si tiene un atributo total, tendrá un método total_changed? y un método total_was que devuelve el valor anterior.

No hay necesidad de agregar nada a su modelo para realizar un seguimiento de esto nunca más.

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

+0

Pensé que algo así existía. ¿Puede vincular a la documentación relevante sobre attr_changed? y attr_was? –

+0

Claro, agregué un enlace a la respuesta. –

0

En primer lugar, debe hacer esto en una transacción para asegurarse de que sus datos se escriben juntos.

Para responder a su pregunta, puede simplemente establecer una variable miembro con el valor anterior en la actualización anterior, que luego puede acceder en la actualización posterior, sin embargo, esta no es una solución muy elegante.

+0

Tengo dificultades para ver cómo implementaría lo que quiero con una transacción. ¿Una transacción como esta vive en el modelo o en el controlador? ¿Elimino mis devoluciones de llamada 'after_update' y 'before_update'? Finalmente, ¿cómo obtengo el valor anterior que necesito para determinar la diferencia? – Abel

+0

No importa, veo que tengo que colocar el código en el controlador. estamos bien. – Abel

0

Idea 1: Envolver la actualización en una transacción de base de datos, de manera que si falla la actualización de sus totales no se cambia la tabla: ActiveRecord Transactions docs

Idea 2: esconder el valor antiguo en @old_total durante el before_update.

2

Agregar a su modelo:

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

A continuación, utilice @old_amount en el código after_update.

8

Algunas otras personas están mencionando todo esto en una transacción, pero creo que se hizo por usted; solo necesita desencadenar la reversión al generar una excepción para los errores en las devoluciones de llamada after_ *.

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

Toda la cadena de devolución de llamada de un ahorre, ahorre !, o destruir carreras de llamadas dentro de una transacción. Eso incluye after_ * hooks. Si todo va bien, se ejecuta COMMIT una vez que la cadena se ha completado.

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

4

ActiveRecord::Dirty es un módulo que está integrado en ActiveRecord para rastrear los cambios de los atributos. Entonces puede usar thing.amount_was para obtener el valor anterior.

6

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"]} 
+1

Creo que la última línea debería leer 'person.previous_changes' en lugar de' person.changes' –

5

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

Estos métodos se llaman dirty methods métodos.

¡Salud!

Cuestiones relacionadas