3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsでcollection_check_boxesを使い中間テーブルに複数レコードを登録する

Posted at

はじめに

実装したい内容としては、タイトルの通りです。
考えてみれば辺り前だなということだったのですが、アプリケーションを作成中に少しハマったので、忘れない用に書き残します。
Railsは便利ですが、故に最初は詳細な部分が分かりづらいことがあると

環境

  • Ruby 2.7.2
  • Rails 6.0.3.4

要件

要件を再度整理します。

  1. 親子関係にある複数のモデルを一度にフォームから登録したい。
  2. 子のモデルの方は、チェックボックスで複数選択をさせて登録させたい。

モデル関係

※ 記事の説明に必要な最低限の情報のみ記載

shopモデル

カラム 備考
id integer pk
shop_name string

tasteモデル

ショップで扱う系統(アメカジ、ストリート、カジュアル etc..)を格納するマスタデータ

カラム 備考
id integer pk
style string

shop_tasteモデル

shopモデルとtasteモデルの情報を格納する中間モデル

カラム 備考
id integer pk
shop_id integer fk (参照先:shopモデルのid )
taste_id integer fk (参照先:tasteモデルのid )

関連付け

shop.rb内で、同一のフォームで登録したいモデルに対して、accepts_nested_attributes_forを指定する。

  • shopモデル
class Shop < ApplicationRecord
  has_many :shop_tastes, dependent: :destroy
  has_many :tastes, through: :shop_tastes

  accepts_nested_attributes_for :shop_tastes, allow_destroy: true
end
  • tasteモデル
class Taste < ApplicationRecord
  has_many :shop_tastes, dependent: :destroy
  has_many :shops, through: :shop_tastes
end

  • shop_tasteモデル
class ShopStyle < ApplicationRecord
  belongs_to :shop
  belongs_to :taste
end

controllerとview

以下が今回ハマった部分のコードになります。

shops_controller.rb
  def shop_params
    params.require(:shop).permit( :shop_name, taste_attributes[taste_ids:[]])
  end
new.html.erb

<%= form_with model: @shop, local: true do |f| %>

    <% f.fields_for :shop_tastes do |fc| %>
      <%= fc.collection_check_boxes :taste_ids, Taste.all, :id, :style, checked: @shop.taste_ids ,include_hidden: false do |b| %>
        <%= b.check_box %>
        <%= b.text %>
      <% end %>
    <% end %>

    <%= f.submit %>
<% end %>

ハマった理由とポイント

上記のコードで、フォームに値を入力して、送信を行うと unkown attributes taste_ids for ...と、そんな属性はない、という旨のエラーメッセージが出力されてしまいました。
自分では、小モデルの複数登録の場合collection_check_boxesfields_forを基本的にセットで使用すると考えていました。

しかしcollection_check_boxes自体パラメータを送信する時に配列の形式で送信を行うため、XXX_idsのようなパラメータ名を指定する必要があります。

API docfields_forを見ると、以下の用に記述がありました。

Like form_for, it yields a FormBuilder object associated with a particular model object to a block, and within the block allows methods to be called on the builder to generate fields associated with the model object.

ざっくり日本語訳すると、以下のような内容です。

form_for と同様に、特定のモデルオブジェクトに関連付けられた FormBuilder オブジェクトをブロックに生成し、ブロック内でビルダーにメソッドを呼び出してモデルオブジェクトに関連付けられたフィールドを生成することができます。

確かに、shop_tasteモデルにはtaste_idsというフィールドは存在しないため、エラーになるのは当たり前ですね。。。
自分の中で、親子関係にある複数モデルを一気に登録する場合、fields_forは必須だと勘違いしていたのですがそうではありませんでした。

class ShopsController < ApplicationController

  def new
    @shop = Shop.new
    @shop.shop_tastes.build
  end

  def create
    @shop = Shop.new(shop_params)
    @shop.save
  end

  private
  def shop_params
    params.require(:shop).permit( :shop_name, taste_ids:[])
  end
end

以下正常に動いたコード


<%= form_with model: @shop, local: true do |f| %>
    <%= f.label :shop_name %>
    <%= f.text_field :shop_name, class: "form-control" %>

    <%= f.collection_check_boxes :taste_ids, Taste.all, :id, :style, checked: @shop.style_ids ,include_hidden: false do |b| %>
      <%= b.check_box %>
      <%= b.text %>
    <% end %>

    <%= f.submit %>
<% end %>

正直fields_forについては、詳細な部分まで調べきれておらず細かい仕様が把握できていないので、今後時間のある時に調べます。

3
8
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
3
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?