after_saveでレコードが作成または更新されたかどうかを確認する方法


95

#new_record?関数は、レコードが保存されているかどうかを判断します。ただし、after_saveフックでは常にfalse です。レコードが新しく作成されたレコードであるか、更新から古いレコードであるかを判別する方法はありますか?

before_createモデルにフラグを設定したり、データベースに別のクエリを要求したりするなど、別のコールバックを使用しないことを望んでいます。

任意のアドバイスをいただければ幸いです。

編集:それを決定する必要がafter_saveフック、そして私の特定のユースケースのため、ノーがあるupdated_atか、updated_onタイムスタンプ


1
ええと、おそらくbefore_saveでパラメータを渡しますか?大声で考える
2011

回答:


167

これをafter_saveコールバックに使用することを考えていました。

より簡単な解決策は、を使用することですid_changed?(で変更されないためupdate)、またはcreated_at_changed?タイムスタンプ列が存在する場合でも使用できます。

更新:@mitsyが指摘するように、このチェックがコールバックの外で必要な場合は、を使用しますid_previously_changed?docsを参照してください。



6
after_updateとafter_createで区別するのが最善です。コールバックは、それが作成であるか更新であるかを示すために引数を取る共通のメソッドを共有できます。
matthuhiggins 2013年

2
これは変更された可能性があります。少なくともRails 4では、after_saveコールバックはafter_createまたはafter_updateコールバックの後に実行されます(guides.rubyonrails.org/active_record_callbacks.htmlを参照)。
マーク

3
これらのフィールドのどちらもチェックしないと、の外では機能しませんafter_save
fatuhoku

3
id_changed?レコードが保存された後は(少なくともフックの外では)falseになります。その場合、使用できますid_previously_changed?
mltsy

30

私が知っているレールマジックはここにはありません。あなたは自分でそれをしなければなりません。仮想属性を使用してこれをクリーンアップできます...

モデルクラス:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end

26

人のためのさらに別の選択肢、行う必要がありupdated_at、タイムスタンプを:

if created_at == updated_at
  # it's a newly created record
end

悪い考えではありませんが、状況によっては逆効果になる可能性があります(必ずしも弾丸の証拠とは限りません)。
アッシュブルー

マイグレーションの実行時期によっては、created_atおよびupdated_atレコードがオフになる場合があります。また、最初にレコードを保存した直後に誰かがレコードを更新する可能性が常にあります。これにより、時間が同期しなくなる可能性があります。それは悪い考えではなく、より弾丸の証拠となる実装を追加できるように感じるだけです。
アッシュブルー

また、レコードが最初に作成されてからかなり後のこともあります。レコードが数か月更新されない場合、作成さればかりのように見えます。
bschaeffer 2014

1
@bschaeffer申し訳ありませんが、私の質問は、「最初に作成されたとき以外のいつでもコールバックでcreated_at同等updated_atにすることは可能after_saveですか?」でした。
colllin

1
@colllin:レコードを作成する場合created_atupdated_atで等しくなるafter_saveコールバック。他のすべての状況では、それらはafter_saveコールバックで等しくありません。
bschaeffer 2014

22

保存された後after_create、レコードが新しいレコードである場合にのみ呼び出されるコールバックがあります。これが変更および保存された既存のレコードである場合に使用するコールバックもあります。いずれかの後にコールバックは、両方のケースで呼び出されるかが呼ばれています。after_updateafter_saveafter_createafter_update

after_create新しいレコードが保存された後に1度実行する必要がある場合に使用します。

詳細はこちら:http : //api.rubyonrails.org/classes/ActiveRecord/Callbacks.html


1
ねえ、答えをありがとう、これは私を大いに助けました。乾杯の男、+ 10 :)
アダムマッカーサー、

1
これは実際には当てはまらないかもしれません。作成に関連付けがある場合、関連付けが作成される前にafter_createが呼び出されるため、すべてが作成されていることを確認する必要がある場合は、after_save
Niels Kristian

18

オブジェクトはすでに保存されているため、以前の変更を確認する必要があります。IDは作成後にのみ変更する必要があります。

# true if this is a new record
@object.previous_changes[:id].any?

インスタンス変数もあります@new_record_before_save。次の方法でアクセスできます。

# true if this is a new record
@object.instance_variable_get(:@new_record_before_save)

どちらもかなり見苦しいですが、オブジェクトが新しく作成されたかどうかを確認できます。お役に立てば幸いです。


私は現在、after_saveRails 4を使用したコールバックでbyebugにいますが、どちらもこの新しいレコードを識別するために機能していません。
MCB 2015年

それ@object.previous_changes[:id].any?はかなりシンプルでエレガントです。これは、レコードが更新された後に機能します(私はから呼び出しませんafter_save)。
thekingoftruth 2016

1
@object.id_previously_changed?少し醜いです。
aNoble

@MCBをafter_saveon Rails 4に追加したい場合は、のchanges[:id]代わりに確認してくださいprevious_changes[:id]。ただし、これはRails5.1で変更されています(github.com/rails/rails/pull/25337の説明を参照
gmcnaughton

previous_changes.key?(:id)より良い理解のために。
セバスチャンパルマ

2

Rails 5.1以降の方法:

user = User.new
user.save!
user.saved_change_to_attribute?(:id) # => true

1

Rails 4(4.2.11.1でチェック)の場合、changesおよびの結果は、内のオブジェクト作成時にprevious_changes空のハッシュ{}になりますafter_save。のattribute_changed?ようなメソッドid_changed?は期待どおりに機能しません。

しかし、この知識を利用してchanges、更新時に少なくとも1つの属性が必要であることを知って、changes空かどうかを確認できます。空であることを確認したら、オブジェクトの作成中にいる必要があります。

after_save do
  if changes.empty?
    # code appropriate for object creation goes here ...
  end
end

0

:idは通常は変わらないはずだと知っていても具体的になりたいのですが、

(byebug) id_change.first.nil?
true

非常に奇妙な予期しないバグを見つける代わりに、具体的にする方が常に安上がりです。

true信頼できない議論からの旗を期待するのと同じ方法

def foo?(flag)
  flag == true
end

これは、奇妙なバグに座らないようにする多くの時間を節約します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.