Help us understand the problem. What is going on with this article?

Ruby on Rails accepts_nested_attributes_for 使い方

More than 1 year has passed since last update.

概要

ネストしたモデル間で、
親モデル作成時に子モデルも一緒にデータを保存しちゃおうってやつ

モデルがぐちゃぐちゃしてくると、同じform_for内で複数のモデルのレコードを保存したくなる時があります。そんな時に便利なメソッド。

例えばDiscモデルとSongモデルがあるとします。

Discモデル

class Disc < ApplicationRecord
  belongs_to :item, dependent: :destroy
  has_many :songs

Songモデル

class Song < ApplicationRecord
  belongs_to :disc, inverse_of: :songs
end

このようにDiscモデルとSongモデルが1対多だったとします。
(1枚のDiscは複数のSongを持つ)

こんな時に1つのform_for内でいっぺんに2つのレコードを作成できちゃうのが
accepts_nested_attributes_for

実装していくよ

Discモデル

class Disc < ApplicationRecord
  belongs_to :item, dependent: :destroy
  has_many :songs
  accepts_nested_attributes_for :songs, allow_destroy: true
end

Songモデル

class Song < ApplicationRecord
  belongs_to :disc, inverse_of: :songs
end

Discsコントローラー

(今回はDiscsコントローラでSongも保存という感じ)

class DiscsController < ApplicationController
  before_action :set_disc, only: [:update]

  def new
    @item = Item.find(params[:item_id])
    @disc = @item.discs.build
    15.times { @disc.songs.build }
  end

  def create
    @item = Item.find(params[:item_id])
    @disc = @item.discs.build(disc_params)
    if @disc.save
      redirect_to @disc
    else
      render :new
    end
  end

  def update
    if @disc.update(disc_params)
      redirect_to adimin_path
    else
      render :edit
    end
  end

  private
  def disc_params
    params.require(:disc).permit(:disc_list, songs_attributes: [:id, :song_title, :time, :song_list])
  end
  def set_disc
    @disc = Disc.find_by(item_id: params[:item_id])
  end
end

(ちなみに今回ルーティングはItem->Disc->Songというネスト構造になってます)

ストロングパラメーターでsongs_attributesとして子モデルを渡しているのも肝ですね

ビュー(discs/new)


<div class="container">
  <%= form_for @disc, url: item_discs_path do |f| %>
     <div class="form-group">
         <%=f.label :ディスク番号 %>
         <%= f.text_field :disc_list %>
     </div>
   <div class="song-form">
    <%= f.fields_for :songs do |s| %>
       <div class="form-group">
          <%= s.label :曲順 %>
          <%= s.text_field :song_list %>
          <%= s.label :曲名 %>
          <%= s.text_field :song_title %>
          <%= s.label :タイム %>
          <%= s.text_field :time %>
       </div>
    <% end %>
  </div>  
  <%= f.submit "作成する", class: "btn btn-info" %>
 <% end %>   
</div>

form_for内でf.fields_forとして、新しいブロック変数に渡しているのが肝です。

今回コントローラ内で

15.times { @disc.songs.build }

としていますが、これだと毎回15曲保存されてしまうので、
accepts_nested_attributes_forと相性が良いgemcocconを使うのが良さそうです。

注意

Qiita初投稿のため、また、データベース設計やルーティング設計はプロダクトによって異なるのであくまで参考までに。
間違っている箇所があったらご指摘していただけると有り難いです!

まとめ

Rails最高!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away