This page looks best with JavaScript enabled

【Rails】ネストされたattributes_paramsをフォームからコントローラに渡す

 ·   ·  ☕ 5 分で読めます
✏️

はじめに

フォームからネストされた値を渡し、ストロングパラメータとして取得するだけのサンプルコードです。
フォームオブジェクトはフォームオブジェクトでいつかまとめたいと思っている。

バージョン

  • Ruby3.0.0
  • Rails 6.1.3

アソシエーション

Product -< Medium
Product >-< Technology

こんな感じで、Productを基準としてそれぞれ一対多・多対多のアソシエーションです。

senses_erb

View

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
= form_with model: @product, do |f|
  = f.label :title
  = f.text_field :title

  = f.label :summary
  = f.text_area :summary

  /  多対多のアソシエーション
  = f.label :technology_ids
  = f.select :technology_ids, Technology.order(:id).pluck(:name, :id), { include_blank: 'なし' }, { multiple: true }

  /  一対多の子レコードを同時に作成する
  - 2.times do |i|
    = f.fields_for 'media_attributes[]', Medium.new do |e|
      = e.label :title
      = e.text_field :title

      = e.label :description
      = e.text_area :description

    = f.submit

(コード例ではclassなどは省略しています)

image

① 多対多のアソシエーション

そもそも、f.labelf.selectの第一引数には、モデルのゲッターメソッドのシンボルを入れることができます。そして、値がフォームのvalueに入ります。

こんな感じ。

1
2
3
4
5
class Product < ApplicationRecord
  def hoge
    'ほげですよ'
  end
end
1
2
  = f.label :hoge
  = f.text_area :hoge

image

なので、@product.title@product.descriptionが呼ばれているのと同じように、@product.technology_ids(関連technologyのidの配列)を呼んでいるようなものです。

このtechnology_idsは、Productにhas_many technologiesを定義することで、Productのインスタンスメソッドとして使えるようになります。

collection_singular_idsメソッドは、そのコレクションに含まれるオブジェクトのidを配列にしたものを返します。
@book_ids = @author.book_ids

Active Record の関連付け - Railsガイド

② 一対多の子レコードを同時に作成する

2.times do |i|の繰り返しの部分は、JSで要素を追加する処理を行うのがいいのかなと思います。(Hotwireでの実装例
今回は子レコードのattributesを配列で渡すサンプルなので、こういう風にゴリ押しな書き方をしています。

fields_forについては、ドキュメントの説明とコード例が「はい」って感じなので、以下を参照してください。

モデルを固定してフォームを生成
form_for内で異なるモデルを編集できるようになる

fields_for | Railsドキュメント

引数の'media_attributes[]'は、コントローラに配列を送るための書き方です。media_attributesという命名はaccepts_nested_attributes_forに倣っています。
Railsは、フォームのname要素からparamsを組み立てるので、このように指定します。

Railsは、重複したパラメータ名を無視します。パラメータ名に空の角かっこ[ ]が含まれている場合、パラメータは配列の中にまとめられます。たとえば、電話番号入力時に、複数の電話番号を入力できるようにしたい場合、フォームに以下を置くことができます。

Action View フォームヘルパー - Railsガイド

image

Params

image

フォームにこのように記述し、値を送ります。コントローラで習得したparamsはこんな感じです。

1
2
3
4
5
6
7
8
    20: def create
    21:   binding.pry
 => 22: end

[1] pry(#<ProductsController>)> params
=> #<ActionController::Parameters{"authenticity_token"=>"WsUUeJ4TWugMGw5MgYgUZw8d3DdwyzCo03ZuDimySqbmB3c3uMkVlvH_r4oU7BZ0PEuGypb1ntsgFofZ4q9Ozw", "product"=>{"title"=>"ほげ","summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[{"title"=>"メディア名", "description"=>""},{"title"=>"", "description"=>""}]}, "commit"=>"登録する", "controller"=>"products", "action"=>"create"} permitted: false>
[2] pry(#<ProductsController>)> params[:product]
=> #<ActionController::Parameters {"title"=>"ほげ", "summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[{"title"=>"メディア名", "description"=>""}, {"title"=>"", "description"=>""}]} permitted: false>

params[:product]を整形するとこんな感じです↓↓
ちゃんとネストされた形でパラメータが形成されていることがわかります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "title"=>"ほげ",
  "summary"=>"サービス概要",
  "technology_ids"=>["", "2", "51", "52"],
  "media_attributes"=>[
    {
      "title"=>"メディア名",
      "description"=>""
    }, {
      "title"=>"",
      "description"=>""
    }
  ]
}

そして、これをストロングパラメータで取得する場合は、以下のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class ProductsController < ApplicationController
  # 省略

  private

  def product_params
    params.require(:product).permit(
      :title,
      :summary,
      technology_ids: [],
      media_attributes: [:title, :description]
    )
  end
end
1
2
[3] pry(#<ProductsController>)> product_params
=> #<ActionController::Parameters {"title"=>"ほげ", "summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[#<ActionController::Parameters {"title"=>"メディア名", "description"=>""} permitted: true>, #<ActionController::Parameters {"title"=>"", "description"=>""} permitted: true>]} permitted: true>

params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])

この宣言では、name、emails、friends属性が許可されます。

Action Controller の概要 - Railsガイド

accepts_nested_attributes_forを使っている場合はこのままProduct.create(product_params)で関連レコードごと作成できます(今回は配列の2つ目がバリデーションエラーで失敗しますが)。
しかし、accepts_nested_attributes_forを使わない場合はフォームオブジェクトで値をよしなにする必要があります。

To Be Continued…ということで、この記事ではここまでです。

Share on

END
END
@aiandrox

目次