ActiveRecordのアソシエーションは1度取得するとキャッシュしてくれるため、2度目からはSQLが発行されません。
この機能はとても便利なのですが、たまに最新の情報にリロードしたいときがあります。
例えば下記のようにuser.profile.addressが別で更新された場合です。
irb(main):001:0> user = User.first
# profileは初回アクセスなので、SQLが発行される
irb(main):003:0> user.profile.address
Profile Load (0.9ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 2 LIMIT 1
=> "住所だよ"
# profileはキャッシュしているのでSQLは発行されない
irb(main):004:0> user.profile.address
=> "住所だよ"
# 別プロセスでprofile.addressを更新
irb(main):005:0> Profile.find(1).update!(address: '住所だよ!!!!!')
Profile Load (1.6ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`id` = 1 LIMIT 1
TRANSACTION (0.7ms) BEGIN
User Load (0.9ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Profile Update (1.1ms) UPDATE `profiles` SET `profiles`.`address` = '住所だよ!!!!!', `profiles`.`updated_at` = '2021-10-29 07:44:20.414663' WHERE `profiles`.`id` = 1
TRANSACTION (3.5ms) COMMIT
=> true
# キャッシュから取得するため、更新されていない
irb(main):006:0> user.profile.address
=> "住所だよ"
そんなときに2つのやり方があります。
1つ目はActiveRecordのreload
を使います。
reloadを使うと、下記のようにSQLが発行されて最新の情報に更新されます。
irb(main):007:0> user.profile.address
=> "住所だよ"
irb(main):008:0> user.profile.reload
Profile Load (0.9ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`id` = 1 LIMIT 1
=> #<Profile:0x000055ba8153a7a0 id: 1, user_id: 2, address: "住所だよ!!!!!", created_at: Wed, 20 Oct 2021 02:12:12.898532000 UTC +00:00, updated_at: Fri, 29 Oct 2021 07:44:20.414663000 UTC +00:00>
irb(main):009:0> user.profile.address
=> "住所だよ!!!!!"
2つ目はアソシエーションを設定したら自動生成されるreload_#{アソシエーション名}
です。
has_oneとbelongs_toのどちらにも同様に生成されます。
irb(main):013:0> user.profile.address
=> "住所だよ"
irb(main):014:0> user.reload_profile
Profile Load (0.9ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 2 LIMIT 1
=> #<Profile:0x000055ba8206ec48 id: 1, user_id: 2, address: "住所だよ!!!!!", created_at: Wed, 20 Oct 2021 02:12:12.898532000 UTC +00:00, updated_at: Fri, 29 Oct 2021 08:04:07.284792000 UTC +00:00>
irb(main):015:0> user.profile.address
=> "住所だよ!!!!!"
上記の通り、データが存在しているときはどちらを使っても同じように再取得されるのですが、削除されていた場合は挙動が違うので注意が必要です。
reload
を使った場合
irb(main):018:0> Profile.find(1).destroy
Profile Load (1.1ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`id` = 1 LIMIT 1
Profile Destroy (1.1ms) DELETE FROM `profiles` WHERE `profiles`.`id` = 1
irb(main):019:0> user.profile.reload
Profile Load (0.8ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`id` = 1 LIMIT 1
/usr/local/bundle/gems/activerecord-6.1.4.1/lib/active_record/relation/finder_methods.rb:357:in `raise_record_not_found_exception!': Couldn't find Profile with 'id'=1 (ActiveRecord::RecordNotFound)
reload_#{アソシエーション名}
を使った場合
irb(main):003:0> Profile.find(1).destroy
Profile Load (0.7ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`id` = 1 LIMIT 1
Profile Destroy (0.9ms) DELETE FROM `profiles` WHERE `profiles`.`id` = 1
irb(main):004:0> user.reload_profile
Profile Load (0.8ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 2 LIMIT 1
=> nil
reload
を使った場合、データが見つからないとActiveRecord::RecordNotFound
が発生します。
一方、reload_#{アソシエーション名}
を使った場合はデータが見つからないとnilになります。
削除される可能性があるデータを扱う場合はご注意ください!!