Search code examples
ruby-on-railsformsactiverecordactionview

Rails complex nested forms with 3 models


This question concerns three models:

Sale

class Sale < ActiveRecord::Base
  has_many :sale_items
  has_many :items, through :sale_items
end

Item

class Item < ActiveRecord::Base
  has_many :sale_items
  has_many :sales, :through => :sale_items
end

SaleItem

class SaleItem < ActiveRecord::Base
  belongs_to :sale
  belongs_to :item
end

To explain, an item acts as a base template for a sale_item. The application has many Items, but these are not necessarily a part of every Sale. So, sale_item.name actually points to sale_item.item.name, and sale_item's price method looks like this:

def price
  super || item.price
end

A sale_item either gets its price from its item, or that price can be overridden for that specific sale_item by editing its price column in the database.

This is what I'm having difficulty with in my sales/_form.html.erb view: I essentially need a table of all Item objects that looks the table in this Tinkerbin: http://tinkerbin.com/46T7JAKs.

So, what that means is that if an unchecked checkbox gets checked and the form is submitted, a new SaleItem needs to be created with an item_id equal to that if the Item from the list, and with appropriate price and quantity fields (quantity is specific to SaleItem and does not exist for Item objects).

Additionally, if the Sale that is being edited already includes a specific SaleItem, that checkbox should already be checked when the form view is rendered (so unchecking a box for a row would delete the SaleItem object associated with that Item and this Sale).

I'm not sure how this could be done—maybe I'm doing it wrong from the beginning. I toyed with the idea of doing away with the SaleItem model altogether and just creating a items_sales table with the fields sale_id, item_id, price, and quantity, but I'm not sure that is the best pattern.


Update

The previous solution I posted ended up with some flaws and failing tests. I finally figured it out, but will post the real solution shortly.


Solution

  • What you want to have is a checkbox tag with an array which will hold on submission an array of all selected id's, so instead of the html checkbox use the checkbox helper:

    <%= check_box_tag 'sale_item_ids[]', item.id -%>
    

    On submission the params hash will hold the ids of the selected item's. What you need to do now is loop on each of these and do the appropriate creation of the relationship (sale_item). You must also loop on those that exist already for this sale and delete them if they are not in the array submited.

    Upon creating the actual html page you can check if the id of the checkbox is already in the sale and check/uncheck it accordingly.

    Hope this helps :)