【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_to
にtouch: 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
で囲ってあげる。
以上になります。