コード日進月歩

しんくうの技術的な小話、メモ、つれづれ、など

ActiveRecordで保存前の値を取りたいときはカラム名_before_last_saveというものがある

ActiveRecordのDirty系小ネタ

環境

$ bin/rails -v
Rails 5.2.2

使い方例

今回はUserにnameというカラムがあるという形の例で説明

ActiveRecordでオブジェクトを作る、この時点ではsaveされてないのでnil

u = User.new(name:"Taro")
#=> #<User id: nil, name: "Taro", created_at: nil, updated_at: nil>
u.name
# => "Taro"
u.name_before_last_save
#=> nil

save処理を行う、これを行うとsaveされるが以前のものは無いのでnil

u.save
   (0.2ms)  BEGIN
  User Create (1.0ms)  INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES ('Taro', '2019-03-23 02:25:16', '2019-03-23 02:25:16')
   (19.7ms)  COMMIT
# => true
u.name
#=> "Taro"
u.name_before_last_save
#=> nil

saveすると、以前の値を取ることができる

u.name = "Ziro"
#=> "Ziro"
u.name
#=> "Ziro"
u.name_before_last_save
# => nil
u.save
   (0.2ms)  BEGIN
  User Update (20.0ms)  UPDATE `users` SET `name` = 'Ziro', `updated_at` = '2019-03-23 02:25:41' WHERE `users`.`id` = 13
   (19.8ms)  COMMIT
#=> true
u.name_before_last_save
#=> "Taro"

当たり前ですが、新たに引き直した場合は取れない

# idが13のものは先程のレコードなので直指定で取得
new_read_u = User.find(13)
  User Load (23.0ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 13 LIMIT 1
# => #<User id: 13, name: "Ziro", created_at: "2019-03-23 02:25:16", updated_at: "2019-03-23 02:25:41">
new_read_u.name_before_last_save
# => nil

注意点

似たようなものに{{カラム名}}_wasがあるが、振る舞いが違うので気をつける。

u.name
# => "Ziro"
u.name = "Saburo"
# => "Saburo"

# Saburoに変える前はZiroなのでZiro
u.name_was
# => "Ziro"
u.name_before_last_save
# => "Taro"

# Saburoは保存されていないのでwasはZiroのまま
u.name = "Shiro"
# => "Shiro"
u.name_was
# => "Ziro"
u.name_before_last_save
# => "Taro"

参考リンク