Определите, какие атрибуты были изменены в обратном вызове Rails after_save? - PullRequest
133 голосов
/ 05 октября 2010

Я настраиваю обратный вызов after_save в моем наблюдателе модели для отправки уведомления, только если атрибут опубликованный модели был изменен с false на true.Поскольку такие методы, как изменились? , полезны только перед сохранением модели, я сейчас (и безуспешно) пытаюсь сделать это следующим образом:

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

Кто-нибудьесть какие-либо предложения относительно лучшего способа справиться с этим, предпочтительно с помощью обратных вызовов наблюдателя модели (чтобы не загрязнять код моего контроллера)?

Ответы [ 8 ]

167 голосов
/ 05 октября 2010

В вашем after_update фильтре для модели, которую вы можете использовать _changed? аксессор (по крайней мере, в Rails 3, не уверен в Rails 2) Так, например:

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

Это просто работает.

153 голосов
/ 18 декабря 2013

Для тех, кто хочет знать только что сделанные изменения в обратном вызове after_save:

Rails 5.1 и выше

model.saved_changes

Rails <5.1 </h2> model.previous_changes Также см .: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes

46 голосов
/ 03 августа 2017

Любой, кто увидит это позже, так как в настоящее время (август 2017 г.) возглавляет Google: стоит упомянуть, что это поведение будет изменено в Rails 5.2 и имеет предупреждения об устаревании, начиная с Rails 5.1, как ActiveModel :: Dirty немного изменилось.

Что мне изменить?

Если вы используете метод attribute_changed? вafter_* -callbacks, вы увидите предупреждение вроде:

ПРЕДУПРЕЖДЕНИЕ ОБ УСТРАНЕНИИ: Поведение attribute_changed? внутри обратных вызовов после будет меняться в следующей версии Rails.Новое возвращаемое значение будет отражать поведение вызова метода после возврата save (например, противоположное тому, что он возвращает сейчас).Чтобы сохранить текущее поведение, используйте saved_change_to_attribute? вместо этого.(вызывается из some_callback в /PATH_TO/app/models/user.rb:15)

Как уже упоминалось, это можно легко исправить, заменив функцию на saved_change_to_attribute?.Так, например, name_changed? становится saved_change_to_name?.

Аналогично, если вы используете attribute_change, чтобы получить значения до и после, это также изменится и выдаст следующее:

ПРЕДУПРЕЖДЕНИЕ ОТКЛОНЕНИЯ: Поведение attribute_change внутри обратных вызовов после будет меняться в следующей версии Rails.Новое возвращаемое значение будет отражать поведение вызова метода после возврата save (например, противоположное тому, что он возвращает сейчас).Чтобы сохранить текущее поведение, используйте вместо этого saved_change_to_attribute.(вызывается из some_callback в /PATH_TO/app/models/user.rb:20 )

Опять же, как упоминалось, метод меняет имя на saved_change_to_attribute, что возвращает ["old", "new"].или используйте saved_changes, который возвращает все изменения, и к ним можно получить доступ как saved_changes['attribute'].

46 голосов
/ 27 мая 2012

Если вы сможете сделать это на before_save вместо after_save, вы сможете использовать это:

self.changed

возвращает массив всех измененных столбцов в этой записи.

Вы также можете использовать:

self.changes

, который возвращает хэш столбцов, которые изменились и до и после результатов в виде массивов

7 голосов
/ 10 февраля 2012

«Выбранный» ответ не работал для меня.Я использую рельсы 3.1 с CouchRest :: Model (на основе Active Model).Методы _changed? не возвращают true для измененных атрибутов в хуке after_update, только в хуке before_update.Я смог заставить его работать, используя (новый?) 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
4 голосов
/ 15 августа 2017

вы можете добавить условие к after_update следующим образом:

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

  ...
end

нет необходимости добавлять условие в сам метод send_notification.

0 голосов
/ 14 декабря 2012

Я использую это для извлечения хэша с новыми значениями атрибута, что полезно для обновления других моделей

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

* * * * * * * * * * * * * * * * * *

* * * * *значение false, где присвоение возвращает false, а "hash" не возвращается.

Я думаю, что есть более простой способ, я новичок в rails

0 голосов
/ 05 октября 2010

Вы просто добавляете аксессор, который определяет, что вы меняете

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
...