Determinar qué atributos se cambiaron en Rails after_save devolución de llamada?
-
27-09-2019 - |
Pregunta
Soy la creación de una devolución de llamada after_save en mi modelo de observador para enviar una notificación sólo si el modelo de publicó atributo fue cambiado de falso a verdadero. Dado que los métodos tales como cambiado sólo son útiles antes de guardar el modelo, la forma en que estoy actualmente (y sin éxito) tratando de hacerlo es la siguiente:?
def before_save(blog)
@og_published = blog.published?
end
def after_save(blog)
if @og_published == false and blog.published? == true
Notification.send(...)
end
end
¿alguien tiene alguna sugerencia en cuanto a la mejor manera de manejar esto, utilizando preferentemente las devoluciones de llamada modelo de observación (con el fin de no contaminar mi código controlador)?
Solución
En su filtro after_update
del modelo que puede de acceso de uso _changed?
(al menos en los carriles 3, no está seguro de los carriles 2). Así, por ejemplo:
class SomeModel < ActiveRecord::Base
after_update :send_notification_after_change
def send_notification_after_change
Notification.send(...) if (self.published_changed? && self.published == true)
end
end
Simplemente funciona.
Otros consejos
Para los que quieren saber los cambios que acaba de hacer en una devolución de llamada after_save
:
Barras de 5,1 y una mayor
model.saved_changes
Rieles <5,1
model.previous_changes
También vea: http://api.rubyonrails.org /classes/ActiveModel/Dirty.html#method-i-previous_changes
Para que nadie vea esto más adelante, ya que actualmente (Agosto 2017) encabeza Google: Vale la pena mencionar, que este comportamiento se altera en Rails 5.2 , y tiene como advertencias de obsolescencia de los carriles 5.1, como ActiveModel :: Dirty cambió un poco.
¿Qué puedo cambiar?
Si está utilizando el método attribute_changed?
en los after_*
-devoluciones de llamada, verá una advertencia como:
Deprecation ADVERTENCIA: El comportamiento de
attribute_changed?
dentro de devoluciones de llamada después se van a cambiar en la próxima versión de Rails. El nuevo valor de retorno reflejará el comportamiento de una llamada al método después volviósave
(por ejemplo, lo contrario de lo que devuelve ahora). Para mantener el comportamiento actual, el usosaved_change_to_attribute?
lugar. (Llamado a partir de some_callback en /PATH_TO/app/models/user.rb:15)
Como se menciona, se podría solucionar esto fácilmente mediante la sustitución de la función con saved_change_to_attribute?
. Así, por ejemplo, se convierte en name_changed?
saved_change_to_name?
.
Del mismo modo, si usted está utilizando la attribute_change
para obtener los valores de antes y después, esto cambia también y lanza la siguiente:
Deprecation ADVERTENCIA: El comportamiento de
attribute_change
dentro de devoluciones de llamada después se van a cambiar en la próxima versión de Rails. El nuevo valor de retorno reflejará el comportamiento de una llamada al método después volviósave
(por ejemplo, lo contrario de lo que devuelve ahora). Para mantener el comportamiento actual, el usosaved_change_to_attribute
lugar. (Llamado a partir de some_callback en /PATH_TO/app/models/user.rb:20)
De nuevo, como se menciona, el método cambia de nombre a saved_change_to_attribute
que devuelve ["old", "new"]
.
o uso saved_changes
, que devuelve todos los cambios, y estos pueden ser accedidos como saved_changes['attribute']
.
En caso de que usted puede hacer esto en vez de before_save
after_save
, usted será capaz de utilizar este:
self.changed
devuelve una matriz de todas las columnas cambiado en este registro.
También puede usar:
self.changes
que devuelve un hash de columnas que cambió y antes y después de los resultados como matrices
La respuesta "seleccionado" no funcionó para mí. Estoy usando los carriles 3.1 con CouchRest :: Modelo (Basado en Modelo activo). Los métodos _changed?
no devuelven cierto para los atributos modificados en el gancho after_update
, sólo en el gancho before_update
. Yo era capaz de conseguir que funcione con el (nuevo?) Gancho around_update
:
class SomeModel < ActiveRecord::Base
around_update :send_notification_after_change
def send_notification_after_change
should_send_it = self.published_changed? && self.published == true
yield
Notification.send(...) if should_send_it
end
end
Puede agregar una condición a la after_update
este modo:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
no hay necesidad de añadir una condición dentro del método send_notification
sí.
Estoy usando esto para extraer un hash con los nuevos valores de atributos, que es útil para mí para actualizar otros modelos
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
La
attr[1].last == false
es necesaria cuando el nuevo valor es false
, donde los rendimientos de asignación falsa y "control" no se devuelve.
Creo que hay una manera más fácil, soy nuevo en rieles
Usted acaba de añadir un descriptor de acceso que defina lo que cambia
class Post < AR::Base
attr_reader :what_changed
before_filter :what_changed?
def what_changed?
@what_changed = changes || []
end
after_filter :action_on_changes
def action_on_changes
@what_changed.each do |change|
p change
end
end
end