怎麼判斷model在update的時候哪些欄位被改變了?
2016-02-29 11:20:11

How

privous_changes

首先要知道model本身提供了一個很好用的methodprevious_changes,當model被save時,previous_changes會列出哪些欄位被改了,而且新舊值都會被記錄下來。

product = Product.create(name: "aaa")
product.update_attributes(name: "bbb")
product.previous_changes # {"name"=>["aaa", "bbb"], "updated_at"=>[Mon, 29 Feb 2016 03:25:36 UTC +00:00, Mon, 29 Feb 2016 03:25:54 UTC +00:00]}

不過我們現在有個需求是當某個欄位的值改變時,要去同時更新其它model的資料。如果每次save時都要記得去呼叫previous_changes,然後判斷欄位是否有更新,再去做對應的事情。這本身就很麻煩。有沒有可以在save時就自動去做更新呢?

after_commit

上面的需求很明顯就要用到model的callback。儲存相關的callback有 after_update after_save after_commit,結果發現只有在after_commit時previous_changes才能得到正確的結果,所以只要把判斷欄位是否有更新的程式放在after_commit就可以了。

after_commit在測試上的坑

當然要寫個測試來測試能不能正常執行,結果發現竟然不行。原來是測試的時候是不做commit的,詳情可以參考這個篇SO Testing after_commit with RSpec and mocking。解法是在測試中save完之後還要記得呼叫 run_callbacks(:commit)。這個問題聽說會在rails5被修掉。

UPDATE

建議使用ActiveRecord::Observer取代model的callback,請看下一篇。

Refs