【Rails】アソシエーションで紐づいたレコードを自動保存


概要

中級者は知ってて当然なので、初心者向けです🔰

アソシエーションで関連付けられたモデルを保存する時に、片方のレコードに外部キーが設定されているせいで、「保存処理を2回に分けなきゃ」と思ったことはないですか?(保存するまでidが確定しないので)

そんなときどうするのというお話です

※ 自分もかつて良くないコードを書いていたので、同じような初心者の参考になってもらえれば幸いです

具体例

例えば本のモデル(Book)と著者のモデル(Author)があると想定します
book has_many authorsの関係)

Bookモデル📕

カラム 補足
id integer
title string

Authorモデル

カラム 補足
id integer
book_id integer 外部キー
name string

このとき初心者がやりがちなのが以下の書き方

books_controller
# createアクション

book = Book.new(book_params)
if book.save
  author = Author.new(book_id: book.id, name: author_params[:name])
    if author.save
      # 保存後の処理
    else
      # 失敗時の処理
    end
else
  # 失敗時の処理
end

# book_params, author_paramsの定義は省略してます

これだと以下のようにデメリットが大きいです

  • if文の階層が深くなる
  • bookだけ保存されてauthorが保存されない場合が出てしまう

早期リターンやtransactionでこれらを回避することもできますが、わざわざそんな面倒なことをしなくて済む方法があるのです✨(以下)

books_controller
# createアクション

book = Book.new(book_params)
author = book.build_author(author_params)
return ['保存成功時の処理'] if book.save

# 失敗時の処理

この場合bookの保存が成功した場合はauthorの保存も保証してくれます(片方だけ保存されることはない)

当然外部キーは関連付けられて保存されます
すっきりしていて見やすく、ファットコントローラ対策にもなります😍
※ 早期リターンでリファクタもしてます

モデルの関係性が、belongs_tohas_manyとで書き方が変わるので注意が必要です(以下参照)

belongs_toで関連付けられたモデルのbuild

book.build_author

has_oneでの関連付けもこちらになります

has_manyで関連付けられたモデルのbuild

author.books.build

  • 1つだけ紐づくモデル
    => build_[モデル名の単数形]

  • 複数紐づくモデル
    => [モデル名の複数形].build

と覚えとけば良さそうです😇
build_複数形にするとインスタンスが複数buildされるイメージがつくからこのように分けたんですかね🤔)

参考