Determinare quali attributi sono stati modificati nel callback Rails after_save?
-
27-09-2019 - |
Domanda
Sto impostando una richiamata after_save nel mio osservatore del modello per inviare una notifica solo se il modello pubblicato l'attributo è stato modificato da falso a vero.Poiché metodi come cambiato? sono utili solo prima che il modello venga salvato, il modo in cui sto attualmente (e senza successo) tentando di farlo è il seguente:
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
Qualcuno ha qualche suggerimento sul modo migliore per gestire questo problema, preferibilmente utilizzando i callback dell'osservatore del modello (in modo da non inquinare il codice del mio controller)?
Soluzione
Nel tuo after_update
filtra in base al modello tu Potere utilizzo _changed?
accessor (almeno in Rails 3, non sono sicuro per Rails 2).Quindi ad esempio:
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
Funziona e basta.
Altri suggerimenti
Per chi vuole conoscere le modifiche appena apportate in an after_save
richiamare:
Rails 5.1 e versioni successive
model.saved_changes
Rotaie < 5.1
model.previous_changes
Vedi anche: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes
A chiunque lo vedrà più tardi, come è attualmente (Agosto).2017) supera Google:Vale la pena ricordare che questo comportamento verrà modificato Rotaie 5.2, e presenta avvisi di deprecazione a partire da Rails 5.1, come Modello attivo::Sporco cambiato un po'.
Cosa cambio?
Se stai usando attribute_changed?
metodo nel after_*
-callback, vedrai un avviso come:
AVVISO DI DEPRECAZIONE:Il comportamento di
attribute_changed?
i callback interni e successivi cambieranno nella prossima versione di Rails.Il nuovo valore restituito rifletterà il comportamento della chiamata successiva al metodosave
restituito (es.il contrario di ciò che restituisce adesso).Per mantenere il comportamento corrente, utilizzaresaved_change_to_attribute?
Invece.(chiamato da some_callback su /PATH_TO/app/models/user.rb:15)
Come menzionato, puoi risolvere questo problema facilmente sostituendo la funzione con saved_change_to_attribute?
.Quindi, ad esempio, name_changed?
diventa saved_change_to_name?
.
Allo stesso modo, se stai utilizzando il file attribute_change
per ottenere i valori prima-dopo, anche questo cambia e genera quanto segue:
AVVISO DI DEPRECAZIONE:Il comportamento di
attribute_change
i callback interni e successivi cambieranno nella prossima versione di Rails.Il nuovo valore restituito rifletterà il comportamento della chiamata successiva al metodosave
restituito (es.il contrario di ciò che restituisce adesso).Per mantenere il comportamento corrente, utilizzaresaved_change_to_attribute
Invece.(chiamato da some_callback su /PATH_TO/app/models/user.rb:20)
Ancora una volta, come menzionato, il metodo cambia nome in saved_change_to_attribute
che ritorna ["old", "new"]
.o utilizzare saved_changes
, che restituisce tutte le modifiche ed è possibile accedervi come saved_changes['attribute']
.
Nel caso in cui tu possa farlo before_save
invece di after_save
, potrai utilizzare questo:
self.changed
restituisce un array di tutte le colonne modificate in questo record.
puoi anche usare:
self.changes
che restituisce un hash di colonne modificate e i risultati prima e dopo come matrici
La risposta "selezionata" non ha funzionato per me.Sto utilizzando i binari 3.1 con CouchRest::Model (basato su Active Model).IL _changed?
i metodi non restituiscono true per gli attributi modificati nel file after_update
gancio, solo nel before_update
gancio.Sono riuscito a farlo funzionare utilizzando il (nuovo?) around_update
gancio:
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
puoi aggiungere una condizione al file after_update
così:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
non è necessario aggiungere una condizione all'interno del file send_notification
metodo stesso.
Lo sto usando per estrarre un hash con i nuovi valori degli attributi, che mi è utile per aggiornare altri modelli
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
IL
attr[1].last == false
è necessario quando è presente un nuovo valore false
, dove l'assegnazione restituisce false e "hash" non viene restituito.
Immagino che ci sia un modo più semplice, sono nuovo ai binari
Devi solo aggiungere una funzione di accesso che definisce ciò che cambi
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