web-dev-qa-db-ja.com

Rails after_saveコールバックで変更された属性を確認しますか?

モデルのpublished属性がfalseからtrueに変更された場合にのみ通知を送信するように、モデルオブザーバーでafter_saveコールバックを設定しています。 changed?などのメソッドは、モデルが保存される前にのみ有用であるため、私が現在(および失敗)しようとしている方法は次のとおりです。

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

これを処理する最善の方法について、誰かが提案していますか?できればモデルオブザーバーコールバックを使用してください(コントローラーコードを汚染しないように)?

135
modulaaron

モデルのafter_updateフィルターでは、can_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

それはただ動作します。

168
Radek Paviensky

after_saveコールバックで行われた変更を知りたい場合:

Rails 5.1以降

model.saved_changes

Rails <5.1

model.previous_changes

参照: http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-previous_changes

161
Jacek Głodek

後でこれを見る人には、現在(2017年8月)Googleを上回っています:この動作はRails 5.2で変更されることに言及する価値があります、および ActiveModel :: Dirty が少し変更されたRails 5.1の時点で非推奨の警告があります。

何を変更しますか?

attribute_changed?-コールバックでafter_*メソッドを使用している場合、次のような警告が表示されます。

非推奨の警告:コールバック後のattribute_changed?の動作は、Railsの次のバージョンで変更されます。新しい戻り値は、saveが返された後のメソッド呼び出しの動作を反映します(たとえば、現在返されているものの反対)。現在の動作を維持するには、代わりにsaved_change_to_attribute?を使用します。 (/PATH_TO/app/models/user.rb:15のsome_callbackから呼び出されます)

言及されているように、関数をsaved_change_to_attribute?に置き換えることで簡単に修正できます。したがって、たとえば、name_changed?saved_change_to_name?になります。

同様に、attribute_changeを使用してbefore-after値を取得している場合、これも同様に変更され、以下をスローします。

非推奨の警告:コールバック後のattribute_changeの動作は、Railsの次のバージョンで変更されます。新しい戻り値は、saveが返された後のメソッド呼び出しの動作を反映します(たとえば、現在返されているものの反対)。現在の動作を維持するには、代わりにsaved_change_to_attributeを使用します。 (/PATH_TO/app/models/user.rb:20のsome_callbackから呼び出されます)

繰り返しますが、言及したように、このメソッドは名前をsaved_change_to_attributeに変更し、["old", "new"]を返します。または、すべての変更を返すsaved_changesを使用します。これらはsaved_changes['attribute']としてアクセスできます。

55
Frederik Spang

before_saveの代わりにafter_saveでこれを実行できる場合、これを使用できます。

self.changed

このレコードで変更されたすべての列の配列を返します。

以下も使用できます。

self.changes

変更された列のハッシュを返し、結果の前後に配列として返します

46
zeacuss

「選択された」答えは私にとってはうまくいきませんでした。 CouchRest :: Model(Active Modelに基づく)でRails 3.1を使用しています。 _changed?メソッドは、after_updateフックで変更された属性に対してtrueを返さず、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
7
Jeff Gran

次のようにafter_updateに条件を追加できます。

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

  ...
end

send_notificationメソッド自体に条件を追加する必要はありません。

4
echo

これを使用して、新しい属性値でハッシュを抽出します。これは、他のモデルを更新するのに役立ちます

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を返し、「ハッシュ」が返されない場合に必要です。

もっと簡単な方法があると思う、私はRailsを初めて使う

0
Maragues