自分用メモ
gem 'nested_form_fields'
gemをインストール。
このgemをインストールするとfields_forが使えるようになる。
fields_forは親以下の子に使うform_forのようなモノ。
fields_forを使うときは親よりもネストさせて書く。
モデル関係
親:post
子:program
子:dealer
孫:program
has_many :programs, inverse_of: :post, dependent: :destroy
has_one :dealer
accepts_nested_attributes_for :programs, allow_destroy: true
accepts_nested_attributes_for :dealer, allow_destroy: true
postから見て子供であるprogramとdealerのアソシエーションを書く。
accepts_nested_attributes_forを書くことによって、モデル同士が関連付けられている時に、ネストさせることで一度にまとめて保存できる。
allow_destroy:trueをオプションに書くことによって、fields_forで保存した値を削除できるようになる。(この認識はちょっとあやふやです。)
※注意点
モデルはアソシエーションで単数なのか複数なのかきちんと意識すること。
最初dealerの記述をする時に、accepts_nested_attributes_for :dealers, allow_destroy: true と書いてしまいうまくいかなかった。
モデルのdealerはhas_oneなので「s」はいらない。
belongs_to :company
belongs_to :post, optional: true
dealerに紐づく子供はいないのでこれだけ。
今回の話にcompanyは関係ないので割愛。
belongs_to :post, optional: true
has_many :products, inverse_of: :program, dependent: :destroy
accepts_nested_attributes_for :products, allow_destroy: true
programから見て親のpostと子供のproductとのアソシエーション。
postと同じようにaccepts_nested_attributes_forを記載。
belongs_to :program, optional: true
belongs_to :category
productに子供はいないのでこれだけ。
今回の話にcategoryは関係ないので割愛。
コントローラー
def new
@post = Post.new
dealers = @post.build_dealer
programs = @post.programs.build
products = programs.products.build
end
def create
@post = Post.new(post_params)
@post.save
end
private
def post_params
params.require(:post).permit(:facility_name, :address, :tell, :registration_date, :estimate_sheet, :delivery_date, :delivery_note, :postal_code, :kananame, :user_id, dealer_attributes:[:name, :kananame, :company_id, :post_id, :_destroy, :id], programs_attributes:[:software, :user, :post_id, :_destroy, :id, products_attributes:[:thing, :category_id, :model_number, :program_id, :_destroy, :id]]).merge(user_id: current_user.id)
end
大元の親であるposts_controllerのみにnew、createを定義するだけで大丈夫。
ポイントは以下。
・newアクションで子供、孫のbuildを作成する。
今回で言えば、dealer、program、productのbuild作成。
・paramsのpermitではモデルの記述でaccepts_nested_attributes_forの対象の子は、
dealer_attributes:という風に モデル名_attributes:と記述する。
・また、子供、孫のハッシュをかっこでネストさせる。
今回のdealerに子供はいないのでとじかっこの位置には注意。
programには子供であるproductがいるので、更にネストさせている。
・また、モデルでallow_destroy: trueを記載しているモデルにはpermitの中に:_destroy と :idを記述する。
・createアクションでは、モデルオブジェクトの作成はPost.new(post_params)と大元の親だけで大丈夫。
変数@postに作成したモデルオブジェクトを入れ、@post.saveとすることで保存される。
### View
= render 'partial/sidebar'
.main
%span.main-child
情報を登録してください
.main-registration__date
.main-registration__date-child
登録日
= form_for @post, url: posts_path do |f|
=f.text_field :registration_date, placeholder: "登録日", type: "text", class:"registration__date-input-text"
.main-facility
.main-facility-child
名前
=f.text_field :facility_name, placeholder: "名前", type: "text", class:"facility-input-text"
.main-kana
.main-kana-child
名前(カナ)
=f.text_field :kananame, placeholder: "カナ", type: "text", class:"kana-input-text"
.top
%span.top-name
ディーラーを登録してください
.top-dealer
.top-dealer-child
= f.fields_for :dealer do |k|
.top-dealer-child_box
.top-dealer-child_box-company_text
会社名
.top-dealer-child_box-company
= k.collection_select :company_id, Company.all, :id, :name
.top-dealer-child_box-name_text
名前
.top-dealer-child_box-name
= k.text_field :name, placeholder: "名前", type: "text", class:"dealer-input-text", id: "dealer_text"
.top-dealer-child_box-kana_text
名前(カナ)
.top-dealer-child_box-kana
= k.text_field :kananame, placeholder: "カナ", type: "text", class:"dealer_kana-input-text", id: "dealer_kana_text"
.best
%span.best-name
納品内容を登録してください
.best-software
.best-software-child
ソフトウェア
= f.fields_for :programs do |d|
#best-software-child_box
= d.text_field :software, placeholder: "name", type: "text", class:"program-input-text", id: "program_text"
.products
.products-text_box
= d.fields_for :products do |s|
.products-text_box-category_text
カテゴリー
.products-text_box-category
= s.collection_select :category_id, Category.all, :id, :name
.products-text_box-product_text
ハードウェア
.products-text_box-product
= s.text_field :thing, placeholder: "products", type: "text", class:"products-input-text", id: "product_text"
.footer
= f.submit "送信", class:"products_button"
本当のコードは長すぎるので一部削除。
form_forを使って、まず親であるpostを設定。
form_forの書き方
= form_for モデル, url: createのpath do |f|
モデル部分にモデルオブジェクトをいれる、今回は@post。
url部分にはpostのcreateアクションのpathをいれる(コマンドrails routesでわかる)
続いてpostの子供であるdealerを設定。
子供以下は全てfields_forを使う。
fields_forの書き方
= f.fields_for :モデル do |k|
・モデル部分にシンボルを使ってモデル名を記載。
ここでも単数なのか複数なのか意識すること。
・fields_forの前のfはpostのform_forで使っている変数の|f|←コイツ
必ず親の変数をつけること。
・hamlの場合必ず親のform_forやfields_forよりも中にネストさせること。
※programのビューの塊がdealerのfields_forよりネストされていない理由はprogramがdealerと同じ位置の子供だから。
もしprogramがdealerの子供であればネストさせないといけない。
###fields_forさえ使いこなせれば、大抵の親子関係の値は一度に保存可能なはず。