Определите, какие атрибуты были изменены в обратном вызове Rails after_save?
Я настраиваю обратный вызов 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
Есть ли у кого-нибудь какие-либо предложения относительно наилучшего способа справиться с этим, предпочтительно используя обратные вызовы модели-наблюдателя (чтобы не загрязнять мой код контроллера)?
Ответы
Ответ 1
В вашем фильтре after_update
на модели вы можете использовать _changed?
accessor (по крайней мере, в 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
Он просто работает.
Ответ 2
Для тех, кто хочет знать изменения после сохранения, вы должны использовать
model.previous_changes
это работает как model.change
, но работает еще после model.save
и т.д.
Я нашел эту информацию полезной, поэтому, возможно, вы тоже.
В Rails 5.1 + это устарело. Вместо этого используйте saved_changes
в after_save
обратных вызовах.
Ответ 3
Если вы можете сделать это на before_save
вместо after_save
, вы сможете использовать это:
self.changed
он возвращает массив всех измененных столбцов в этой записи.
вы также можете использовать:
self.changes
который возвращает хеш столбцов, которые были изменены, и до и после результатов в виде массивов
Ответ 4
Всем, кто увидит это позже, так как в настоящее время (август 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']
.
Ответ 5
"Выбранный" ответ не сработал у меня. Я использую rails 3.1 с CouchRest:: Model (на основе Active Model). Методы _changed?
не возвращают true для измененных атрибутов в крюке after_update
, только в hook before_update
. Мне удалось заставить его работать с помощью (new?) 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
Я использую это, чтобы извлечь хэш с новыми значениями атрибутов, что полезно для меня, чтобы обновить другие модели.
attributes_changed = self.changes.inject(Hash.new){|hash,attr| ((hash[attr[0].to_sym] = attr[1].last) || attr[1].last == false) && hash}
attr[1].last == false
требуется, когда новое значение false
, где присваивание возвращает false, а "hash" не возвращается.
Я предполагаю, что есть более простой способ, я новичок в рельсах
Ответ 7
вы можете добавить условие к after_update
так:
class SomeModel < ActiveRecord::Base
after_update :send_notification, if: :published_changed?
...
end
нет необходимости добавлять условие внутри самого метода send_notification
.
Ответ 8
Вы просто добавляете аксессуар, который определяет, что вы меняете.
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