Подтвердить что ты не робот

Определите, какие атрибуты были изменены в обратном вызове 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

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

4b9b3361

Ответ 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