if it does't challenge you, it can't change you

RubyのWebエンジニアやってます。主にRuby on Railsのことで勉強になったこと書いていきます。

一つのフォームで複数のモデルに保存する

したいこと

今回は映画のレビューを投稿するreview投稿フォーム画面に、 映画の好きなフレーズを投稿できるphrase投稿フォームも表示し、 一度送信ボタンへ押すだけでreviewはもちろん、そのreviewに紐づいたphraseも保存する。 なので、reviewモデルとそのreviewモデルに紐づいたphraseモデルが存在している。

重要なメソッド

  • field_forメソッド
  • accepts_nested_attributes_for

バージョン

rails 5.1.4

 カラム

reviewsテーブルにはcontentカラム
phrasesテーブルにもcontentカラムのみとする

アソシエーション定義

model/review.rb

  has_many :phrases, dependent: :destroy
  accepts_nested_attributes_for :phrases

model/phrase.rb

belongs_to :review, optional: true

Controller

def new
  review = Review.new
  2.times { @review.phrases.build }
end

def create
  @review = Review.new(review_params)
  @review.save
end

private
def review_params
  params.require(:review).permit(:content,  phrases_attributes: [:content])
end

View

<%= form_with(model: review, local: true) do |form| %>
  <div class="field">
    <%= form.label :content %>
    <%= form.text_area :content, id: :review_content %>
  </div>
  <%= form.fields_for :phrases do |phrase_field| %>
    <div class="field">
      <%= phrase_field.text_area :content, id: :phrase_content %>
    </div>
  <% end %>
  <%= form.submit %>
<% end %>

学んだこと

  • field_forメソッドで異なるモデルへ保存するためのフォームを作成できる。controller#newでtimesメソッドを使ってインタンスを作成しておけば、viewではeach文を使って保存しなくても、(field_forだけではなく)form.fields_forでコントローラーで作成したインスタンス分のフォームを表示してくれる。
  • accepts_nested_attributes_forをモデルに追加することでStrongParameterに記載しているネストされたパラメーター(phrases_attributes: [:content])を読み取り、それぞれのモデルのレコードを更新できる。
  • rails5からはbelongs_to :review, optional: trueにしないと外部キー(〇〇_id)がもしnilの場合、保存ができなくなっているため、optional: trueをつけておくとそれを無効化しておく。ただ、今回はあってもなくても、どちらでも大丈夫。

参考