LoginSignup
49
43

More than 5 years have passed since last update.

accepts_nested_attributes_for を使った入れ子のformが罠だらけな気がする

Last updated at Posted at 2015-10-26

1回すべてのつまづきポイントにつまづけば次からは楽な気がするが...

今回のを試すために書いた全ソース
Release refactoring form · shingo-nakanishi/workout_rails

つまづきポイント1

入れ子を条件によってはINSERTしたくない場合どうしたらよいかわからない。

答え:
reject_ifオプションを使う

つまづきポイント2

そもそも入れ子のformの書き方がわからない。

答え:

class PostsController < ApplicationController
  def new
    @post = Post.new
    @post.post_categories.build
  end

  def create
    @post = Post.new(create_params)
    @post.save!
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end

  def edit
    @post = Post.find(params[:id])
    unless @post.post_categories.present?
      @post.post_categories.build
    end
  end

  def update
    @post = Post.find(params[:id])

    ActiveRecord::Base.transaction do
      @post.update!(update_params)
    end
  rescue ActiveRecord::RecordInvalid
    puts "ERROR"
  end

  private
  def create_params
    params.require(:post).permit(:description, post_categories_attributes: [:post_category_id])
  end

  def update_params
    params.require(:post).permit(:description, post_categories_attributes: [:id, :post_category_id])
  end
end

<%= form_for @post do |f| %>
    <%= f.label :description %><%= f.text_field :description %><br/>
    <%= f.fields_for :post_categories do |b| %>
        <%= b.label :post_category_id %>
        <%= b.collection_select :post_category_id,
                                PostCategoryMaster.all,
                                :id,
                                :name,
                                multiple: true,
                                prompt: 'please select' %>
    <% end -%>
    <%= f.submit %>
<% end %>

参考

いまさら聞けない!? accepts_nested_attributes_forの使い方 - (゚∀゚)o彡 sasata299's blog

つまづきポイント3

Editで入れ子のデータが選ばれなくなったときはDELETEする方法がわからない。

答え:
allow_destroyを使う

class Post < ActiveRecord::Base
  has_many :post_categories, inverse_of: :post
  accepts_nested_attributes_for :post_categories,
                                allow_destroy: true,
                                reject_if: :is_reject

  private

  def is_reject(attributes)
    is_exist = attributes[:id].present?
    is_empty = attributes[:post_category_id].blank?
    attributes.merge!(_destroy: 1) if is_exist and is_empty
    !is_exist and is_empty
  end
end

参考

ruby on rails - Destroy on blank nested attribute - Stack Overflow

削除系についてもっと

【Rails】fields_for と accepts_nested_attributes_for - kzy52's blog

ビューから _destroy を渡せない場合は mark_for_destruction を使う

つまづきポイント4

Has-Many-Through Relationsは一手間加えないとうまくいかない。

答え:
inverse_ofを使う
ふつうの has_manyなときは使う必要ないはず。

参考

accepts_nested_attributes_for with Has-Many-Through Relations

つまづきポイント5

has_manyなやつcountすると思ったのと違う値が返ってくる

オブジェクトを保持している数を表すlengthは2だけど、DB上の数を表すcountは1になっているということ。

参考

accepts_nested_attributes_forの:allow_destroyオプション - akimatter

つまづきポイント6

UPDATEするときは要件に合わせてメソッドを選ぶ(accepts_nested_attributes_forと関係なくそうだが)

参考

Different Ways to Set Attributes in ActiveRecord (Rails 4)

49
43
0

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
49
43