こんにちは。
初めて記事を投稿してみます。
Railsで子レコードを作成・更新時、親レコードのupdated_atも更新したい時
ActiveRecordの関連付けで、touchオプションを使えば簡単にできること知ったので
備忘のためにまとめてみます。
##touchオプション
Railsガイドより
:touchオプションをtrueに設定すると、そのオブジェクトがsaveまたはdestroyされたときに、関連付けられたオブジェクトのupdated_atタイムスタンプやupdated_onタイムスタンプが常に現在の時刻に設定されます。
class Shop < ApplicationRecord
has_many :reviews
end
class Review < ApplicationRecord
belongs_to :shop, touch: true
end
review = Review.last
review.update( title: "いいお店")
=> # review.shop.updated_atも更新する
##利用シーン
###キャッシュの無効化
このためにtouchオプションを使うのが一番多いようです。
Russian Doll Cachingという、ネストされたフラグメントキャッシュの内側のキャッシュ(review)が更新されると、外側のキャッシュ(shop)も更新してくれる機能で、子のmodelにtouchオプションをつければ有効になります。
キャッシュを自動で更新してくれるので便利ですね。
###検知処理の高速化
例えばこんな機能を追加しようとして
- お店(shop)の評点を集計する
- 評点は全レビュー(review)の平均点を使用する
これをバッチ処理で開発する場合
- 差分更新だけにしたい
- 更新対象は、前回バッチ実行時から今まででreview.updated_atが更新されたかで検知したい
touchオプションを付けておくと、review作成・更新時にshopのupdated_atも自動更新してくれるので、バッチ処理ではshopのupdated_atを参照するだけで集計対象を検知できるようになります。
(もしreviewのupdated_atを参照しようとすると、shopとReviewは1:Nなので参照するレコードが多くなり処理が遅くなります)
##touchしたくない場合
touchオプションを付けたものの、なんらかの理由(子レコードを大量作成や、全て更新)でtouchしたくない場合、ActiveRecord.no_touching
で一時的に無効にできます。
shop = Shop.last
ActiveRecord::Base.transaction do
ActiveRecord::Base.no_touching do
shop.reviews.find_each do |review|
# shopのupdated_atは更新されない
review.update!(some_attributes)
end
end
end
##参考URL
https://railsguides.jp/association_basics.html#belongs-to%E3%81%AE%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3-touch
https://postd.cc/the-complete-guide-to-rails-caching/
https://techracho.bpsinc.jp/hachi8833/2018_04_18/55475