Edited at

RailsのActiveRecordでカラムの変更前後の情報を取得

More than 3 years have passed since last update.


はじめに

ActiveRecordで{attr_name}_wasのメソッドをコールバック時に利用してみたので、せっかくだったらと思い以下のメソッド全部の実行結果をまとめてみる。


  • changed?

  • changed

  • changes

  • {attr_name}_changed?

  • {attr_name}_was

  • {attr_name}_change

※この子だけ使う機会がよく分からないので割愛します!


  • {attr_name}_will_change!

メソッド詳しく知りたい方は以下のリンクからソースコードどうぞ

rails/activemodel/lib/active_model/dirty.rb


目的

以下のコールバック時での各メソッドの実行結果を確認する


  • before_save

  • after_save

  • after_commit


試した内容

色々試すように適当なtestモデル作成


app/models/test.rb

# == Schema Information

#
# Table name: tests
#
# id :integer not null, primary key
# score :integer not null
# grade :text(65535) not null
# created_at :datetime not null
# updated_at :datetime not null
#

class Test < ActiveRecord::Base
before_save TestCallbacks.new
after_save TestCallbacks.new
after_commit TestCallbacks.new
end


コールバックはあえて分けて用意(理由は特にない

全てのタイミングでbinding.pryを差し込んでみる


app/models/test_callbacks.rb

class TestCallbacks

def before_save(test)
binding.pry
end

def after_save(test)
binding.pry
end

def after_commit(test)
binding.pry
end
end


以下のようなテストデータ用意してgradeカラムを更新

pry(main)> test = Test.first

+----+-------+-------+---------------------------+---------------------------+
| id | score | grade | created_at | updated_at |
+----+-------+-------+---------------------------+---------------------------+
| 1 | 80 | C | 2016-03-03 18:05:55 +0900 | 2016-03-03 18:05:55 +0900 |
+----+-------+-------+---------------------------+---------------------------+
pry(main)> test.update(grade: 'B')


before_saveのタイミングでの結果

[7] pry(#<TestCallbacks>)> test.grade

"B"
[8] pry(#<TestCallbacks>)> test.changed?
true
[9] pry(#<TestCallbacks>)> test.changed
[
[0] "grade"
]
[10] pry(#<TestCallbacks>)> test.changes
{
"grade" => [
[0] "C",
[1] "B"
]
}
[11] pry(#<TestCallbacks>)> test.grade_changed?
true
[12] pry(#<TestCallbacks>)> test.score_changed?
false
[13] pry(#<TestCallbacks>)> test.grade_was
"C"
[14] pry(#<TestCallbacks>)> test.grade_change
[
[0] "C",
[1] "B"
]


after_saveのタイミングでの結果

※updated_atカラムが更新されようとしている以外は変わらない

pry(#<TestCallbacks>)> test.grade

"B"
pry(#<TestCallbacks>)> test.changed?
true
pry(#<TestCallbacks>)> test.changed
[
[0] "grade",
[1] "updated_at"
]
pry(#<TestCallbacks>)> test.changes
{
"grade" => [
[0] "C",
[1] "B"
],
"updated_at" => [
[0] Thu, 03 Mar 2016 18:05:55 JST +09:00,
[1] Thu, 03 Mar 2016 19:03:51 JST +09:00
]
}
pry(#<TestCallbacks>)> test.grade_changed?
true
pry(#<TestCallbacks>)> test.score_changed?
false
pry(#<TestCallbacks>)> test.grade_was
"C"
pry(#<TestCallbacks>)> test.grade_change
[
[0] "C",
[1] "B"
]


after_commitのタイミングでの結果

pry(#<TestCallbacks>)> test.grade

"B"
pry(#<TestCallbacks>)> test.changed?
false
pry(#<TestCallbacks>)> test.changed
[]
pry(#<TestCallbacks>)> test.changes
{}
pry(#<TestCallbacks>)> test.grade_changed?
false
pry(#<TestCallbacks>)> test.grade_was
"B"
pry(#<TestCallbacks>)> test.grade_change
nil


まとめ



  • {attr_name}_wasのようなメソッドはデータの更新完了後は変更前のデータ取れませんが、after_saveのタイミングであれば変更前のデータ取れるので何かと使えるタイミングが多いんじゃないかなと思いました

  • この辺りのメソッドの面白い活用事例などありましたら教えていただきたいです


    • 特にこの子{attr_name}_will_change!