LoginSignup
7
3

More than 3 years have passed since last update.

Rails 子レコードの作成・更新時に親レコードのupdated_atも更新する

Posted at

こんにちは。
初めて記事を投稿してみます。

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

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3