LoginSignup
9
12

More than 5 years have passed since last update.

accepts_nested_attributes_forを使用した子レコードの保存方法

Last updated at Posted at 2019-04-07

使用が想定される場面

Untitled Diagram.png

  • 例えばHomeモデルとStationモデルで1対多の関係の場合、Homeの作成時にStationもまとめてDB操作したい時。
  • Homeをsaveしようとするタイミングで、Homeのバリデーションに加えてStationのバリデーションも自動で走り、問題なければ両方が作成させる処理をしたい時。
    ※子レコード(この場合はStation)のバリデーションやsaveとか考慮したくない時。 has_many、belongs_to、has_oneの関係で使用出来る。
  • has_manyの場合で子レコードにあたる部分を複数まとめて登録したい時。
    ※controllerでbuildの部分を複数回実行して複数作成しておくと、フォームも自動的に複数個表示させる。

Modelの定義

models/home.rb
has_many :stations, dependent: :destroy
accepts_nested_attributes_for :stations, allow_destroy: true
models/station.rb
belongs_to :home, optional: true

外部キーは、レコードに保存させる場合nilだと、バリデーションが発生してしまう。
つまりは、stationsテーブルは、homeのidが決定していないと正常に保存できない。
つまりは、homesレコードが保存された後でないといけない。
この状況を解消するため、 optional: trueを指定する。

Controllerの定義

def new
  @home = Home.new
  # buildする際は、timesメソッドを使用することで、複数の子レコードをviewに渡すことが出来る。
  2.times{@home.stations.build}
end

def create
  if Home.create(home_params)
    redirect_to root_path
  else
    render 'new'
  end
end


private
 def home_params
    params.require(:home).permit(:name, :price, :address, :year, :content, stations_attributes: [:line, :station, :walk])
  end

  def set_params
    @home = Home.find(params[:id])
  end

  def update_params
    params.require(:home).permit(:name, :price, :address, :year, :content, stations_attributes: 
                                [:line, :station, :walk, :_destroy, :id])
  end

viewの定義

new.html.erb
<%= form_for(@home) do |f| %> |
  <div class="field"> |
    <div>
      <%= f.label :name, "物件名" %> |
    </div>

# 省略

<% @home.stations.each_with_index do |station, index| %>
  <%= f.fields_for :stations, station do |s_f| %>
  <h2>最寄り駅<%= index + 1 %></h2>
  <div class="field">
    <div>
      <%= s_f.label :line, "路線名"  %>
    </div>
    <div>
      <%= s_f.text_field :line %>
    </div>
  </div>
  <div class="field">
    <div>
      <%= s_f.label :station, "駅名"  %>
    </div>
    <div>
      <%= s_f.text_field :station %>
    </div>
  </div>
  <div class="field">
    <div>
      <%= s_f.label :walk, "徒歩分数"  %>
    </div>
    <div>
      <%= s_f.text_field :walk %></div>
  </div>
  <% end %>
<% end %>

時折、fields_forで表示してもうまくフォームが表示されないときがあります。
その時はfields_forの第2引数のところに直接指定する。
※この例だと、station(@home.stationsに当たるもの)を指定する。

accepts_nested_attributes_forを使用せず、Form Objectを使用する場合

https://moneyforward.com/engineers_blog/2018/12/15/formobject/
https://qiita.com/9ro/items/10e3676741e09ffb98bb
- 参考URLはこちら

9
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12