Bestimmen Sie, welche Attribute wurden in Rails after_save Rückruf geändert?
-
27-09-2019 - |
Frage
Ich gründe einen after_save Rückruf in meinem Modell Beobachter auf nur eine Benachrichtigung zu senden, wenn das Modell der veröffentlicht Attribut von false auf true geändert wurde. Da Methoden wie geändert nur dann sinnvoll ist, bevor das Modell gespeichert wird, so, wie ich zur Zeit bin (und erfolglos) versucht, so zu tun, ist wie folgt:
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
Hat jemand irgendwelche Vorschläge in Bezug auf die beste Art und Weise zu handhaben, vorzugsweise Modell Beobachter Rückrufe verwendet (um nicht meinen Controller-Code zu verschmutzen)?
Lösung
In Ihrem after_update
Filter auf dem Modell, das Sie auf können Verwendung _changed?
Accessor (zumindest in Rails 3, nicht sicher für Rails 2). So zum Beispiel:
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
Es funktioniert einfach.
Andere Tipps
Für diejenigen, die die Änderungen nur gemacht in einem after_save
Rückruf wissen wollen:
Rails 5.1 und größer
model.saved_changes
Rails <5.1
model.previous_changes
Siehe auch: http://api.rubyonrails.org /classes/ActiveModel/Dirty.html#method-i-previous_changes
Für jeden, dies später zu sehen, wie es zur Zeit (August 2017) Tops Google: Es ist erwähnenswert, dass dieses Verhalten geändert wird, wird in Rails 5.2 , und hat deprecation Warnungen von Rails 5.1, wie ActiveModel :: Schmutzige ein wenig verändert.
Was ändere ich?
Wenn Sie attribute_changed?
Methode in den after_*
-Rückrufe verwenden, erhalten Sie eine Warnung wie sehen:
deprecation WARNUNG: Das Verhalten von
attribute_changed?
innerhalb der nach Rückrufe werden in der nächsten Version von Rails werden zu ändern. Der neue Rückgabewert wird das Verhalten der Aufruf der Methode reflektieren nachsave
zurück (zum Beispiel das Gegenteil von dem, was es gibt jetzt). Um das aktuelle Verhalten beizubehalten, verwendetsaved_change_to_attribute?
statt. (Genannt von some_callback bei /PATH_TO/app/models/user.rb:15)
Wie erwähnt, können Sie diese leicht beheben, indem Sie die Funktion mit saved_change_to_attribute?
ersetzen. So zum Beispiel, name_changed?
wird saved_change_to_name?
.
Ebenso, wenn Sie die attribute_change
verwenden die Vorher-Nachher-Werte zu erhalten, diese Änderungen auch und führt den folgenden:
deprecation WARNUNG: Das Verhalten von
attribute_change
innerhalb der nach Rückrufe werden in der nächsten Version von Rails werden zu ändern. Der neue Rückgabewert wird das Verhalten der Aufruf der Methode reflektieren nachsave
zurück (zum Beispiel das Gegenteil von dem, was es gibt jetzt). Um das aktuelle Verhalten beizubehalten, verwendetsaved_change_to_attribute
statt. (Genannt von some_callback bei /PATH_TO/app/models/user.rb:20)
Auch hier, wie es erwähnt, ändert sich die Methode Namen saved_change_to_attribute
die ["old", "new"]
zurückgibt.
oder Verwendung saved_changes
, die alle um die Änderungen zurückgibt, und diese können als saved_changes['attribute']
zugegriffen werden.
Falls Sie diese auf before_save
statt after_save
tun können, werden Sie in der Lage sein, diese zu nutzen:
self.changed
es gibt ein Array aller geänderten Spalten in diesem Datensatz.
Sie können auch verwendet werden:
self.changes
, die einen Hash von Spalten kehren das geändert und vor und nach den Ergebnissen als Arrays
Die „ausgewählte“ Antwort nicht für mich arbeiten. Ich bin Schiene 3.1 mit CouchRest :: Modell (auf Basis Active-Modell) verwendet wird. Die _changed?
Methoden nicht zurück wahr für geänderte Attribute im after_update
Haken, nur in dem before_update
Haken. Ich konnte es zur Arbeit kommen mit den (neuen?) around_update
Haken:
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
können Sie eine Bedingung an die after_update
wie so hinzufügen:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
gibt es keine Notwendigkeit, eine Bedingung innerhalb der send_notification
Methode selbst hinzuzufügen.
Ich bin mit diesem einen Hash mit den neuen Attributwerten zu extrahieren, was nützlich ist für mich andere Modelle zu aktualisieren
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
Die
attr[1].last == false
benötigt wird, wenn neuer Wert false
ist, wo die Zuordnung false zurück und „Hash“ nicht zurückgegeben.
Ich denke, es ist ein einfacher Weg, ich bin neu auf Schienen
Sie fügen Sie einfach einen Accessor, die definieren, was Sie ändern
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