Déterminer quels attributs ont été modifiés dans Rails after_save rappel?
-
27-09-2019 - |
Question
Je la mise en place d'un rappel after_save dans mon observateur de modèle pour envoyer une notification que si le modèle de publié attribut a été changé de false à true. Étant donné que des méthodes telles que changé ne sont utiles avant que le modèle est enregistré, la façon dont je suis actuellement (et sans succès) en essayant de le faire est suit comme:
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
Est-ce que quelqu'un a des suggestions quant à la meilleure façon de gérer cela, en utilisant de préférence callbacks observateurs du modèle (pour ne pas polluer mon code de contrôleur)?
La solution
Dans votre filtre after_update
sur le modèle peut accesseur utilisation de _changed?
(au moins dans Rails 3, pas sûr pour Rails 2). Ainsi, par exemple:
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
Il fonctionne.
Autres conseils
Pour ceux qui veulent connaître les changements viennent d'être faites dans un rappel after_save
:
Rails 5.1 et plus
model.saved_changes
Rails <5.1
model.previous_changes
Voir aussi: http://api.rubyonrails.org /classes/ActiveModel/Dirty.html#method-i-previous_changes
Pour quiconque voir cela plus tard, comme actuellement (Aug. 2017) arrive en tête Google: Il est mentionner vaut la peine, que ce comportement sera modifié dans Rails 5.2 , et comporte des avertissements relatifs à l'obsolescence comme Rails 5.1, comme ActiveModel :: sale un peu changé.
Que dois-je changer?
Si vous utilisez la méthode de attribute_changed?
dans les callbacks after_*
, vous verrez un avertissement comme:
deprecation AVERTISSEMENT: Le comportement de
attribute_changed?
intérieur après callbacks vont changer dans la prochaine version de Rails. La nouvelle valeur de retour reflète le comportement d'appeler la méthode aprèssave
retour (par exemple, le contraire de ce qu'il retourne maintenant). Pour maintenir le comportement actuel, l'utilisationsaved_change_to_attribute?
à la place. (Appelé à partir de some_callback à /PATH_TO/app/models/user.rb:15)
Comme il mentionne, vous pouvez corriger cela facilement en remplaçant la fonction avec saved_change_to_attribute?
. Ainsi, par exemple, name_changed?
devient saved_change_to_name?
.
De même, si vous utilisez le attribute_change
pour obtenir les avant-après les valeurs, cela change aussi bien et jette les éléments suivants:
deprecation AVERTISSEMENT: Le comportement de
attribute_change
intérieur après callbacks vont changer dans la prochaine version de Rails. La nouvelle valeur de retour reflète le comportement d'appeler la méthode aprèssave
retour (par exemple, le contraire de ce qu'il retourne maintenant). Pour maintenir le comportement actuel, l'utilisationsaved_change_to_attribute
à la place. (Appelé à partir de some_callback à /PATH_TO/app/models/user.rb:20)
Encore une fois, comme il le mentionne, la méthode change de nom pour saved_change_to_attribute
qui retourne ["old", "new"]
.
ou l'utilisation saved_changes
, qui renvoie tous les changements, et ceux-ci sont accessibles en saved_changes['attribute']
.
Si vous pouvez le faire sur before_save
au lieu de after_save
, vous serez en mesure d'utiliser ceci:
self.changed
elle retourne un tableau de toutes les colonnes modifiées dans cet enregistrement.
vous pouvez également utiliser:
self.changes
qui retourne un hachage de colonnes qui a changé et avant et après les résultats sous forme de tableaux
Le « sélectionné » réponse ne fonctionne pas pour moi. J'utilise des rails 3.1 avec CouchRest :: Model (basé sur le modèle actif). Les méthodes de _changed?
ne renvoient pas pour les attributs modifiés dans le crochet after_update
, seulement dans le crochet de before_update
. J'ai pu l'obtenir au travail en utilisant le (nouveau?) Crochet 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
vous pouvez ajouter une condition à la after_update
comme ceci:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
il n'y a pas besoin d'ajouter une condition dans la méthode de send_notification
lui-même.
J'utilise ceci pour extraire un hachage avec les nouvelles valeurs d'attributs, ce qui est utile pour moi de mettre à jour les autres modèles
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
attr[1].last == false
est nécessaire lorsque la nouvelle valeur est false
, où les retours d'affectation faux et « hachage » est pas retourné.
Je suppose qu'il ya un moyen plus facile, je suis nouveau sur les rails
Vous venez d'ajouter un accesseur qui définissent ce que vous modifiez
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