Upgrade to Pro — share decks privately, control downloads, hide ads and more …

本当は怖い Rails の `build_xxx` / The Hard Facts of `build_xxx` of Rails

本当は怖い Rails の `build_xxx` / The Hard Facts of `build_xxx` of Rails

megane42

July 30, 2019
Tweet

More Decks by megane42

Other Decks in Programming

Transcript

  1. create の場合 まず新しい blog レコードを保存 次に古い blog レコードの外部キー (user_id) を

    null にする dependent: :destroy のときはここが DELETE になる 結果的に子レコードは 1 つに保たれるので、まあわかる # 2 回目の user.create_blog を実行したときの SQL begin transaction INSERT INTO "blogs" ("user_id", "created_at", "updated_at" commit transaction begin transaction UPDATE "blogs" SET "user_id" = ?, "updated_at" = ? WHERE commit transaction
  2. build の場合 # 1 回目 blog = user.build_blog blog.save #

    2 回目 blog = user.build_blog さて何が起きるでしょう?
  3. 結果 まさかの 更新系 が走る dependent: :destroy の場合は DELETE が走る! このあと結局

    blog.save をしなかった場合 or 失敗した場合、当 然旧レコードは更新されっぱなしのまま build_xxx は DB を更新しない、という直感に反している # 2 回目の blog = user.build_blog を実行したときの SQL begin transaction UPDATE "blogs" SET "user_id" = ?, "updated_at" = ? WHERE commit transaction
  4. 余談 : create_xxx も怖くね? なぜか トランザクションが分かれている INSERT 後の UPDATE or

    DELETE に失敗するとゴミレコードが残る 例えば belongs_to: に optional: true を付け忘れると UPDATE に失敗する これもトランザクション張った方がいいのかも? # 2 回目の user.create_blog を実行したときの SQL (再掲) begin transaction INSERT INTO "blogs" ("user_id", "created_at", "updated_at" commit transaction begin transaction UPDATE "blogs" SET "user_id" = ?, "updated_at" = ? WHERE commit transaction