Railsの更新前に属性のブロックが変更されたかどうかを確認する必要があります3。
street1、street2、都市、州、郵便番号
私は次のようなものを使用できることを知っています
if @user.street1 != params[:user][:street1]
then do something....
end
しかし、そのコードは非常に長くなります。よりクリーンな方法はありますか?
ActiveModel :: Dirty (デフォルトですべてのモデルで利用可能)を確認してください。ドキュメントは本当に優れていますが、次のようなことができます。
@user.street1_changed? # => true/false
これが、複数の属性の変更をチェックする問題を解決した方法です。
attrs = ["street1", "street2", "city", "state", "zipcode"]
if (@user.changed & attrs).any?
then do something....
end
changed
メソッドは、そのオブジェクトに対して変更された属性の配列を返します。
@user.changed
とattrs
は両方とも配列なので、共通部分を取得できます( ary & other ary
メソッドを参照)。交差の結果は配列です。配列でany?
を呼び出すと、少なくとも1つの交差点がある場合にtrueになります。
また、非常に便利なのは、 changed_attributes
メソッドが元の値を持つ属性のハッシュを返し、 changes
が元の値と新しい値を持つ属性のハッシュを返す(配列内) )。
これらのメソッドをサポートしているバージョンのAPIDockを確認できます。
@model.update_attributes()
が変更を隠したため、ActiveModel::Dirty
は機能しませんでした。これが、コントローラーのupdate
メソッドで変更を検出した方法です。
def update
@model = Model.find(params[:id])
detect_changes
if @model.update_attributes(params[:model])
do_stuff if attr_changed?
end
end
private
def detect_changes
@changed = []
@changed << :attr if @model.attr != params[:model][:attr]
end
def attr_changed?
@changed.include :attr
end
多くの属性の変更を検出しようとすると、混乱する可能性があります。おそらくコントローラーでこれを行うべきではありませんが、まあ。
Rails 5.1+コールバックの場合
この投稿を改善し、他の回答を完了するには
最初の回答は、次のRailsバージョン(5.1以降)のコールバックでは非推奨になります。
では、属性が変更されたかどうかを確認する新しい方法はsaved_change_to_attributeを使用することですか?
できるよ:
@user.saved_change_to_street1? # => true/false
より多くの例を見ることができます この投稿ブログで
上記の答えは優れていますが、知識については別のアプローチもあります。オブジェクト(@design)の 'catagory'列の値を変更できます。
@design.changes.has_key?('catagory')
.changesは、列名としてのキーと各列の2つの値[old_value、new_value]を持つ配列としての値を持つハッシュを返します。たとえば、上記のカテゴリは@designの 'ABC'から 'XYZ'に変更され、
@design.changes # => {}
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }
参照用 RORの変更