Określić, jakie atrybuty zostały zmienione w Rails po wywołaniu zwrotnym save?

Konfiguruję wywołanie zwrotne after_save w moim obserwatorze modelu, aby wysłać powiadomienie tylko wtedy, gdy atrybut published modelu został zmieniony z false na true. Od kiedy zmieniły się metody takie jak ? są przydatne tylko przed zapisaniem modelu, sposób, w jaki obecnie (i bezskutecznie) próbuję to zrobić, wygląda następująco:

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

Czy ktoś ma jakieś sugestie co do najlepszego sposobu radzenia sobie z tym, najlepiej używając wywołań zwrotnych model observer (aby nie zanieczyszczać mojego kontrolera kod)?

Author: Frederik Spang, 2010-10-05

8 answers

W Twoim filtrze after_update na modelu możesz użyć accessor (przynajmniej w Rails 3, nie jestem pewien Dla Rails 2). Tak na przykład:

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
To po prostu działa.
 157
Author: Radek Paviensky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-05 08:09:06

Dla tych, którzy chcą poznać zmiany po zapisaniu, należy użyć

model.previous_changes

To działa jak model.change, ale działa nadal po model.save, itp. Znalazłem to informacje przydatne, więc może ty też.

W Rails 5.1 + jest to przestarzałe. Zamiast tego użyj saved_changes w wywołaniach zwrotnych after_save.

 129
Author: Jacek Głodek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-18 09:47:55

W przypadku, gdy możesz to zrobić na before_save zamiast after_save, będziesz mógł użyć tego:

self.changed

Zwraca tablicę wszystkich zmienionych kolumn w tym rekordzie.

Możesz również użyć:

self.changes

Który zwraca hash kolumn, które uległy zmianie oraz przed i po wynikach jako tablice

 45
Author: zeacuss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-04 12:59:51

Do każdego, kto zobaczy to później, jak to obecnie (sierpień. 2017) tops google: warto wspomnieć, że to zachowanie zostanie zmienione w Rails 5.2 i ma ostrzeżenia o deprecacji od Rails 5.1, ponieważ ActiveModel::Dirty nieco się zmieniło.

Co mam zmienić?

Jeśli używasz metody attribute_changed? w wywołaniu after_*-zobaczysz ostrzeżenie takie jak:

Ostrzeżenie o DEPRECACJI: zachowanie attribute_changed? wewnątrz wywołania po będzie zmiana w następnej wersji Rails. Nowa wartość zwracana będzie odzwierciedlać zachowanie wywołania metody po zwróceniu save (np. odwrotność tego, co zwraca teraz). Aby zachować bieżące zachowanie, użyj saved_change_to_attribute?. (wywołane z some_callback w /PATH_TO / app / models / user.rb: 15)

Jak wspomniano, można to łatwo naprawić, zastępując funkcję saved_change_to_attribute?. Na przykład name_changed? staje się saved_change_to_name?.

Podobnie, jeśli używasz attribute_change, aby uzyskać przed-po wartości, to również zmienia i rzuca następujące:

Ostrzeżenie o DEPRECACJI: zachowanie attribute_change wewnątrz wywołań zwrotnych after zmieni się w następnej wersji Rails. Nowa wartość zwracana będzie odzwierciedlać zachowanie wywołania metody po zwróceniu save (np. odwrotność tego, co zwraca teraz). Aby zachować bieżące zachowanie, użyj saved_change_to_attribute. (wywołane z some_callback w /PATH_TO / app / models / user.rb: 20)

Ponownie, jak wspomina, metoda zmienia nazwę na saved_change_to_attribute, która zwraca ["old", "new"]. lub użyć saved_changes, który zwraca wszystkie zmiany, a te mogą być dostępne jako saved_changes['attribute'].

 25
Author: Frederik Spang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-03 19:45:24

"wybrana" odpowiedź nie zadziałała dla mnie. Używam rails 3.1 z Couchrest:: Model (oparty na Active Model). Metody _changed? nie zwracają true dla zmienionych atrybutów w hooku after_update, tylko w hooku before_update. Udało mi się go uruchomić za pomocą (nowego?) around_update hook:

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
 6
Author: Jeff Gran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-10 19:53:32

Możesz dodać warunek do after_update w następujący sposób:

class SomeModel < ActiveRecord::Base
  after_update :send_notification, if: :published_changed?

  ...
end

Nie ma potrzeby dodawania warunku w samej metodzie send_notification.

 2
Author: echo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-15 18:18:23

Używam tego do wyodrębniania hasha z nowymi wartościami atrybutów, co jest dla mnie przydatne do aktualizacji innych modeli

attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}

The

attr[1].last == false

Jest potrzebne, gdy nową wartością jest false, gdzie przypisanie zwraca false i nie zwraca" hash".

Myślę, że jest łatwiejszy sposób, jestem nowy w rails

 0
Author: Maragues,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-14 14:59:55

Wystarczy dodać accesora, który określa co zmieniasz

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
 -14
Author: shingara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-05 08:08:29