Techioz Blog

コレクションの form_for 作成時の Rails エラー

概要

私のシステムには製品があります。また、商品には各店舗の最小/最大在庫があります。 app/views/product/edit で、それを更新する方法を作成しようとしています。 だから私はそのようなことを試しています:

<%= form_for @product.product_stores, :url => {:action => "update_stocks"} do |form| %>
  <% @product.product_stores.each do |e| %>
    <strong><%= e.store.name %></strong> <br/>
    <%= e.id %> <br/>
        
    <div class="row g-3 align-items-center">
      <div class="col-auto">
        <%= form.label :min_stock_days, class: "col-form-label" %>
      </div>
      <div class="col">
        <%= form.text_field :min_stock_days, class: "form-control" %>
      </div>
    </div>
  <% end %>
<% end %>

そして、画面にエラーが表示されます。

undefined method `to_key' for #<ActiveRecord::Associations::CollectionProxy 
[#<ProductStore id: 913, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 24, created_at: "2024-01-17 16:14:53.475034000 -0300", updated_at: "2024-01-17 16:14:53.475034000 -0300">, 
#<ProductStore id: 11617, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 1, created_at: "2024-01-17 16:58:08.503417000 -0300", updated_at: "2024-01-17 16:58:08.503417000 -0300">, 
#<ProductStore id: 11618, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 2, created_at: "2024-01-17 16:58:08.506141000 -0300", updated_at: "2024-01-17 16:58:08.506141000 -0300">, 
#<ProductStore id: 11619, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 3, created_at: "2024-01-17 16:58:08.508833000 -0300", updated_at: "2024-01-17 16:58:08.508833000 -0300">, 
#<ProductStore id: 11620, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 4, created_at: "2024-01-17 16:58:08.511535000 -0300", updated_at: "2024-01-17 16:58:08.511535000 -0300">, 
#<ProductStore id: 11621, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 5, created_at: "2024-01-17 16:58:08.514277000 -0300", updated_at: "2024-01-17 16:58:08.514277000 -0300">, 
#<ProductStore id: 11622, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 6, created_at: "2024-01-17 16:58:08.516974000 -0300", updated_at: "2024-01-17 16:58:08.516974000 -0300">, 
#<ProductStore id: 11623, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 7, created_at: "2024-01-17 16:58:08.519660000 -0300", updated_at: "2024-01-17 16:58:08.519660000 -0300">, 
#<ProductStore id: 11624, min_stock_days: nil, max_stock_days: nil, product_id: 975, store_id: 23, created_at: "2024-01-17 16:58:08.522390000 -0300", updated_at: "2024-01-17 16:58:08.522390000 -0300">]>
Did you mean?  to_set
               to_ary

どうすれば修正できますか? それを実装する正しい方法は何ですか?

@Arup Rakshit に感謝します。部分的に解決しました。

私の解決策は次のとおりです。 models/product.rb に追加します

accepts_nested_attributes_for :product_stores, update_only: true

products_controller.rb に product_params を追加します

product_stores: [:min_stock_days, :max_stock_days]

そしてビュー内で次のように変更されます。

<%= form_for @product, as: :post, method: :post, :url => {:action => "update_stocks"} do |form| %>
  <%= form.fields_for :product_stores, @product.product_stores.each do |ps|%>
    <strong> <%= ps %> </strong>
      <div class="row g-3 align-items-center">
        <div class="col-auto">
          <%= ps.label :min_stock_days, class: "col-form-label" %>
        </div>
        <div class="col">
          <%= ps.text_field :min_stock_days, class: "form-control" %>
        </div>
      </div>

      <div class="row g-3 align-items-center">
        <div class="col-auto">
        <%= ps.label :max_stock_days, class: "col-form-label" %>
      </div>
      <div class="col">
        <%= ps.text_field :max_stock_days, class: "form-control" %>
      </div>
    </div>
  <% end %>

  <div>
    <%= form.submit "Salvar", class: "btn btn-primary" %>
  </div>
<% end %>

さて、私はまだ問題を抱えています… 各最小/最大フィールドがどのストアのものなのかわかりません。 なぜなら、私が座っている店の名前がわからないからです。

解決策

form_for ヘルパー メソッドは、コレクション用ではなく、単一のモデル インスタンス用のフォームを構築するように設計されています。

指定された実装では、ActiveRecord オブジェクトのコレクション (@product.product_stores) で form_for を使用しようとしますが、form_for は単一の ActiveRecord オブジェクトを想定しています。

これを修正するには、@product.product_stores コレクション内のアイテムごとに個別のフォームを作成する必要があります。

たとえば、各 ProductStore をループし、それぞれに個別のフォームを作成します。

<% @product.product_stores.each do |product_store| %>
  <%= form_for product_store, url: {action: "update_stocks"} do |form| %>
    <strong><%= product_store.store.name %></strong> <br/>
    <%= product_store.id %> <br/>
        
    <div class="row g-3 align-items-center">
      <div class="col-auto">
        <%= form.label :min_stock_days, class: "col-form-label" %>
      </div>
      <div class="col">
        <%= form.text_field :min_stock_days, class: "form-control" %>
      </div>
    </div>
    <%= form.submit %>
  <% end %>
<% end %>