Determine quais atributos foram alterados no retorno de chamada do Rails After_Save?
-
27-09-2019 - |
Pergunta
Estou configurando um retorno de chamada posterior no meu modelo para enviar uma notificação apenas se o modelo do modelo Publicados O atributo foi alterado de falso para verdadeiro. Uma vez que métodos como mudado? Só são úteis antes que o modelo seja salvo, a maneira como estou atualmente (e sem sucesso) tentando fazer isso é o seguinte:
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
Alguém tem alguma sugestão sobre a melhor maneira de lidar com isso, de preferência usando retornos de chamada do Model Observer (para não poluir meu código do controlador)?
Solução
Na tua after_update
filtre no modelo que você posso usar _changed?
acessador (pelo menos no Rails 3, não tenho certeza dos trilhos 2). Por exemplo:
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
Apenas funciona.
Outras dicas
Para aqueles que querem saber as mudanças feitas em um after_save
ligue de volta:
Rails 5.1 e maior
model.saved_changes
Rails <5.1
model.previous_changes
Veja também: http://api.rubyonrails.org/classes/activeModel/dirty.html#method-i-previous_changes
Para qualquer pessoa que veja isso mais tarde, como atualmente (agosto de 2017) supera o Google: vale a pena mencionar, que esse comportamento será alterado em Rails 5.2, e tem avisos de depreciação a partir de Rails 5.1, como ActiveModel :: Dirty mudou um pouco.
O que eu mudo?
Se você está usando attribute_changed?
Método no after_*
-Callbacks, você verá um aviso como:
Aviso de depreciação: o comportamento de
attribute_changed?
Dentro de retornos de chamada após a mudança na próxima versão do Rails. O novo valor de retorno refletirá o comportamento de chamar o método depoissave
retornou (por exemplo, o oposto do que retorna agora). Para manter o comportamento atual, usesaved_change_to_attribute?
em vez de. (chamado de algum_callback em /path_to/app/models/user.rb:15)
Como menciona, você pode consertar isso facilmente substituindo a função por saved_change_to_attribute?
. Por exemplo, name_changed?
torna-se saved_change_to_name?
.
Da mesma forma, se você está usando o attribute_change
Para obter os valores anteriores, isso também muda e joga o seguinte:
Aviso de depreciação: o comportamento de
attribute_change
Dentro de retornos de chamada após a mudança na próxima versão do Rails. O novo valor de retorno refletirá o comportamento de chamar o método depoissave
retornou (por exemplo, o oposto do que retorna agora). Para manter o comportamento atual, usesaved_change_to_attribute
em vez de. (chamado de algum_callback em /path_to/app/models/user.rb:20)
Novamente, como menciona, o método muda o nome para saved_change_to_attribute
que retorna ["old", "new"]
. ou uso saved_changes
, que retorna todas as mudanças, e elas podem ser acessadas como saved_changes['attribute']
.
Caso você possa fazer isso em before_save
ao invés de after_save
, você poderá usar isso:
self.changed
Ele retorna uma matriz de todas as colunas alteradas neste registro.
você também pode usar:
self.changes
que retorna um hash de colunas que mudaram e antes e depois dos resultados como matrizes
A resposta "selecionada" não funcionou para mim. Estou usando o Rails 3.1 com o modelo Couchrest :: (com base no modelo ativo). o _changed?
métodos não retornam true para atributos alterados no after_update
gancho, apenas no before_update
gancho. Consegui fazer com que funcione usando o (novo?) around_update
gancho:
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
Você pode adicionar uma condição ao after_update
igual a:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
não há necessidade de adicionar uma condição dentro do send_notification
próprio método.
Estou usando isso para extrair um hash com os novos valores de atributo, o que é útil para eu atualizar outros modelos
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
o
attr[1].last == false
é necessário quando um novo valor é false
, onde a tarefa retorna falsa e "hash" não é devolvida.
Eu acho que há uma maneira mais fácil, sou novo no Rails
Você apenas adiciona um acessador que define o que você muda
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