web-dev-qa-db-ja.com

Rails 3属性が変更されたかどうかを確認します

Railsの更新前に属性のブロックが変更されたかどうかを確認する必要があります3。

street1、street2、都市、州、郵便番号

私は次のようなものを使用できることを知っています

if @user.street1 != params[:user][:street1]
  then do something....
end

しかし、そのコードは非常に長くなります。よりクリーンな方法はありますか?

147
pcasa

ActiveModel :: Dirty (デフォルトですべてのモデルで利用可能)を確認してください。ドキュメントは本当に優れていますが、次のようなことができます。

@user.street1_changed? # => true/false
273
Peter Brown

これが、複数の属性の変更をチェックする問題を解決した方法です。

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

changed メソッドは、そのオブジェクトに対して変更された属性の配列を返します。

@user.changedattrsは両方とも配列なので、共通部分を取得できます( ary & other ary メソッドを参照)。交差の結果は配列です。配列でany?を呼び出すと、少なくとも1つの交差点がある場合にtrueになります。

また、非常に便利なのは、 changed_attributes メソッドが元の値を持つ属性のハッシュを返し、 changes が元の値と新しい値を持つ属性のハッシュを返す(配列内) )。

これらのメソッドをサポートしているバージョンのAPIDockを確認できます。

http://apidock.com/Rails/ActiveModel/Dirty

53
campeterson

@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

多くの属性の変更を検出しようとすると、混乱する可能性があります。おそらくコントローラーでこれを行うべきではありませんが、まあ。

14
hamstar

Rails 5.1+コールバックの場合

この投稿を改善し、他の回答を完了するには

最初の回答は、次のRailsバージョン(5.1以降)のコールバックでは非推奨になります。
では、属性が変更されたかどうかを確認する新しい方法はsaved_change_to_attributeを使用することですか?

できるよ:

@user.saved_change_to_street1? # => true/false

より多くの例を見ることができます この投稿ブログで

5
Lucas Andrade

上記の答えは優れていますが、知識については別のアプローチもあります。オブジェクト(@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の変更

2
shubham mishra