おぴよの気まぐれ日記

おぴよの気まぐれ日記

岡山やプログラミング、ファッションのこと、子育てや人生、生き方についての備忘録。

【Ruby on Rails】子の作成時に親のupdated_atを更新したり更新しなかったりする方法

こんちには。opiyoです。

railsでデータをsaveしたりupdateしたりするとupdated_atが更新されるけど、親データのupdated_atを更新したり、更新しないようにするにはどうすればいいの!?

こんな悩みを解決していきたいと思います。

結論から言ってしまうと

  • 親データのupdated_atを更新する: touch
  • 親データのupdated_atを更新しない: no_touching

これらを使うと簡単に実現することが出来ます!

親データに紐づく子データを夜間処理で作るようなことをしてるんだけど、朝見ると親データのupdated_atが全部夜間の日付になっていました。 何も触ってないのに表示順が変わっていたので何事かと思う経験がありました...

touchの存在は知ってたのですが、no_touchingについては全く知らなかったので改めて一緒に使い方を調べてみました!

本文

事前準備

先ずは親データと子データが1 : Nになるデータを作ります。

# 親データ
create_table "new_users", force: :cascade do |t|
  t.string "name"
  t.integer "age"
end

# 子データ
create_table "posts", force: :cascade do |t|
  t.bigint "new_user_id"
  t.string "title"
  t.text "body"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

親データ: new_user.rb

class NewUser < ApplicationRecord
  has_many :posts, dependent: :destroy
end

子データ: post.rb

class Post < ApplicationRecord
  belongs_to :new_user
end

では、このデータを基に実際に動かしていこうと思います。

railsのモデルでtouchを使って親データのupdated_atも更新する

先ずはtouchを定義します。子データのbelongs_totouch: trueを設定すると、postデータが更新されると勝手にnew_userデータのupdated_atが更新されます。

子データ: post.rb

class Post < ApplicationRecord
  belongs_to :new_user, touch: true
end
> user.updated_at.to_s
=> "2019/12/16 23:23"

> user.posts.create!(title: 'touchを検証するよ')
   (0.2ms)  BEGIN
  Post Create (0.5ms)  INSERT INTO "posts" ("title", "created_at", "updated_at", "new_user_id") VALUES ($1, $2, $3, $4) RETURNING "id"  [["title", "touchを検証するよ"], ["created_at", "2020-01-14 12:59:54.286549"], ["updated_at", "2020-01-14 12:59:54.286549"], ["new_user_id", 16]]
  NewUser Update (0.3ms)  UPDATE "new_users" SET "updated_at" = $1 WHERE "new_users"."id" = $2  [["updated_at", "2020-01-14 12:59:54.288109"], ["id", 16]]
   (1.7ms)  COMMIT

> user.updated_at.to_s
=> "2020/01/14 21:59"

上のログを見て欲しいのですが、postデータをcreateした後に親のnew_userデータをupdateしてるのが分かります!

子データを作成するときにno_touchingを使って親データのupdated_atを更新させない

次はno_touchingを使うとtouchが定義されていてもupdated_atが更新されないことを確認してみようと思います。

使い方は親モデル.no_touching do hoge endです。

> user.updated_at.to_s
=> "2020/01/14 22:05"

> NewUser.no_touching do
*   user.posts.create!(title: 'no_touchingを検証するよ')  
* end  
   (0.2ms)  BEGIN
  Post Create (0.4ms)  INSERT INTO "posts" ("title", "created_at", "updated_at", "new_user_id") VALUES ($1, $2, $3, $4) RETURNING "id"  [["title", "no_touchingを検証するよ"], ["created_at", "2020-01-14 13:07:28.128973"], ["updated_at", "2020-01-14 13:07:28.128973"], ["new_user_id", 16]]
   (1.6ms)  COMMIT

> user.updated_at.to_s
=> "2020/01/14 22:05"

こちらを実行すると先ほど実行されていた、親データのnew_userデータのupdate文は走ってないのが分かると思います!

railsで親のupdated_atを更新したり更新しなかったりする方法まとめ

改めて親データのupdated_atの扱い方法をまとめてみます!

更新する 子モデル側のモデルにbelongs_to new_user, touch trueを付ける

更新しない 子データを更新する際に親モデル.no_touching do hoge endで囲ってあげる。

以上になります。