Note that there are some explanatory texts on larger screens.

plurals
  1. POBest approach to deal with complex nested form
    text
    copied!<p>I have a form with products in rows and suppliers in columns. Users can enter an order quantity for each product / supplier combination.</p> <p><img src="https://i.stack.imgur.com/iCyvo.png" alt="enter image description here"></p> <p>For each Supplier, 1 Order is created. Each Order contains OrderItems. OrderItems are created for each field (Product / Supplier combination) where the user enters a quantity.</p> <p>There's a lot of handwritten code to process the submitted form.</p> <p>Is there any way to DRY up any of the code below? Or is there a better approach altogether?</p> <p>I checked out the Nested Forms Railscast but I don't see any way how I could use accepts_nested_attributes_for here because the input is two dimensional (combination of supplier and product).</p> <pre><code>class Product &lt; ActiveRecord::Base has_many :order_items end class Supplier &lt; ActiveRecord::Base has_many :orders end # groups all OrderItems for 1 Supplier class Order &lt; ActiveRecord::Base has_many :order_items belongs_to :supplier def self.create_orders_and_order_items(orders) orders.each do |supplier_id, order_items| if order_has_order_item?(order_items) order = create!( :total =&gt; 0, :supplier_id =&gt; supplier_id, :order_group_id =&gt; order_group.id ) OrderItem.create_order_items(order, order_items) # update attributes order.update(:total =&gt; order.order_items.sum(:total)) end end end def self.order_has_order_item?(order_items) sum = 0 order_items.each do |product_id, quantity| sum += quantity.to_i end sum &gt; 0 ? true : false end end # 1 OrderItem per product / supplier combination class OrderItem &lt; ActiveRecord::Base belongs_to :order belongs_to :supplier belongs_to :product def self.create_order_items(order, order_items) order_items.each do |product_id, quantity| if quantity.to_i &gt; 0 order_item = create!( :quantity =&gt; quantity, :product_id =&gt; product_id, :order_id =&gt; order.id, ) # update after creating, because methods called below are only available once object has been instantiated order_item.udpate(:total =&gt; order_item.calculate_total) end end end end class OrdersController &lt; ApplicationController def create Order.create_orders_and_order_items(params[:orders]) respond_to do |format| format.html { redirect_to :action =&gt; "index" } end end end # view: _form.html.erb &lt;table&gt; &lt;tr&gt; &lt;td&gt;Name&lt;/td&gt; &lt;% @suppliers.each do |supplier| %&gt; &lt;td COLSPAN=2&gt;&lt;%= supplier.name %&gt;&lt;/td&gt; &lt;% end %&gt; &lt;/tr&gt; &lt;% @products.each do |product| %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= product.name %&gt;&lt;/td&gt; &lt;td&gt;&lt;%#= some price %&gt;&lt;/td&gt; &lt;td&gt;&lt;%= f.text_field "#{supplier.id}[#{product.id}]", :value =&gt; "" %&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/table&gt; &lt;%= f.submit %&gt; # params (from memory) {"orders" =&gt; { "4" =&gt; # supplier_id, 1 Order for each Supplier { "13" =&gt; "2" } # product_id =&gt; quantity, = 1 OrderItem } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload