0

I have a has_many :through form where I can't get an extra attribute to post to the database. I'm fouling up the parameter name somewhere. I can get the foreign keys to post but I have another attribute that I'm trying to track in the join table. Keep in mind this is a 100% ajax based form. Here's what I know

Edit: After researching similar issues, I understand I'm supposed to build the form attributes, but the code I've found doesn't work for some reason. Here are some resources.

http://railsforum.com/viewtopic.php?id=20203

Rails 3, nested multi-level forms and has_many through

What I don't understand is that attaching the product_ids is built into rails. Those values post. Why is it hard to attach the quantity_shipped attribute to that array?

Models and Relationships
  Shipment  has_many :products :through => :product_shipments
  Product   has_many :shipments :through => :product_shipments
  ProductShipments belongs_to :shipment,  belongs_to :product

ProductShipments table
  t.integer    :shipment_id  
  t.integer    :product_id
  t.integer    :qty_shipped  <-- This is the Problem Child

This partial is looped through a few times displaying all the products from a certain vendor. It generates an array for the product_ids and another for the product_shipments quantities.

_product_shipments.html.erb.

<div id="product_shipments_container">
<h3>Assign Products to Ship</h3>
<ul class="product_shipments" id="product_shipments">
  <% Product.by_client(@client_id).each do |product| %>
    <%= content_tag_for :li, product, :value => product.id do %>
      <%= hidden_field_tag("shipment[product_ids][]", product.id) %>
      <%= product.product_name %><%= text_field_tag("product_shipments[qty_shipped]")%> <--This is where the issue lies
    <% end %>
  <% end %>
</ul>
</div>

This is the relevant POST data when the form is submitted

"product_ids"=>["1", "3"]}, "product_shipments"=>{"qty_shipped"=>["32", "23"]}

This is sql that is sent to the database

INSERT INTO `product_shipments` (`product_id`, `qty_shipped`, `shipment_id`) 
VALUES (1, NULL, 155)
INSERT INTO `product_shipments` (`product_id`, `qty_shipped`, `shipment_id`)
VALUES (3, NULL, 155)

Here is the action in my controller

def create
 @shipment = Shipment.new(params[:shipment])

 @product_shipments = @shipment.product_shipments.build(params[:product_shipments])

[:qty_shipped]) <- not correct, but it's what i got so far if @shipment.save respond_with @shipment, :location => shipments_url else flash[:notice]= "Not saved" end end

Here's the last issue I'm having.

TypeError (can't convert Symbol into Integer):
  app/controllers/shipments_controller.rb:24:in `[]'
  app/controllers/shipments_controller.rb:24:in `create'

GOT IT. After making the changes with the correct answer below. I was able to correct the controller to the following

@product_shipments = @shipment.product_shipments.build(params[:product_shipments])
Community
  • 1
  • 1
ctilley79
  • 2,151
  • 3
  • 31
  • 64
  • Do you mean to write `text_field_tag("shipment_products[qty_shipped][]")`? – cdesrosiers Jun 26 '12 at 01:44
  • It didn't seem to make a difference. It still inputs a null value – ctilley79 Jun 26 '12 at 01:51
  • Does your model have the `has_many :shipment_products` and the `accepts_nested_attributes_for :shipment_products` ? Why don't you use `fields_for`? Do you know `simple_form` or `formtastic`? If you want to be adding Shipments dynamically, check out the [cocoon](https://github.com/nathanvda/cocoon) gem. – nathanvda Jun 26 '12 at 10:17
  • yes, they have the associations as listed above. I could use either fields_for or text_field_tag, and I have accepted_nested_attributes. – ctilley79 Jun 26 '12 at 14:12
  • Could you also post the relevant controller action (where you try to create a new shipment)? – cdesrosiers Jun 28 '12 at 02:27
  • Added controller @cdesrosiers – ctilley79 Jun 28 '12 at 02:55

2 Answers2

1

Your 'create' action can be as simple as

def create
  @shipment = Shipment.new(params[:shipment])

  if @shipment.save
    # success
  else
    # failure
  end
end

if you use nested attributes to create shipment_products through the new shipment record. To do this, add the following to the Shipment model

class Shipment
  attr_accessible :shipment_products_attributes
  accepts_nested_attributes_for :shipment_products
end

By using fields_for in the view, this will allow a shipment to accept params[:shipment][:shipment_products_attributes] and pass these on to its shipment_products.

In your new action, you could do something like

def new
  @shipment = Shipment.new
  # collect the ids of the products you want to create shipment products for
  @shipment.shipment_products.build([{:product_id=> ...},{:product_id=> ...}, ...])
end

so that in the form you could do something like

<%= form_for @shipment, :remote => true do |f|%>
  ...
  ...
  <ul>
    <%= f.fields_for :shipment_products do |sp_f| %>
      <li>
        <%= sp_f.text_field :qty_shipped %>
        <%= sp_f.hidden_field :product_id %>
      </li>
    <% end %>
  </ul>
<% end %>
cdesrosiers
  • 8,862
  • 2
  • 28
  • 33
  • In your answer the new action would only work if you know how many products you are going to assign. What if you don't know? The partial is looped through a few times generating all the products that are available for a certain vendor, so the number of products is not finite. The shipment_products in the partial has to be an array – ctilley79 Jun 28 '12 at 12:25
  • Just another quick note. The shipment_id and the product_id post to the database correctly. It's the quantity that is being lost. – ctilley79 Jun 28 '12 at 12:37
  • How exactly are you looping through the partial? – cdesrosiers Jun 28 '12 at 14:25
  • 1
    By the way, fields_for will give you an array of shipment_products here if you build those shipment_products into the the shipment in the new action as shown above. – cdesrosiers Jun 28 '12 at 14:28
  • Sorry this is a lot of info. I do appreciate your help @cdesrosiers – ctilley79 Jun 28 '12 at 15:31
  • I changed the name of the join model. Also added the POST parameters the browser is sending to the create action in the controller to the question above. – ctilley79 Jun 28 '12 at 20:00
  • Sorry if this seems complicated. I think using nested hashes here will generally make your code cleaner, but for now I posted a simpler solution above that should work with your code the way it is now. – cdesrosiers Jun 28 '12 at 21:45
1

The simplest solution is that you need to generate an array of hashes that looks like

:product_shipments=>[{:product_id=>1, :qty_shipped=>32},{:product_id=>3, :qty_shipped=>23}]

instead of two sets of hashes :shipment=>{:product_ids=>[1,3]} and :product_shipments=>[:qty_shipped=>[32,23]].

To do this, change your view code to

<%= hidden_field_tag("product_shipments[][product_id]", product.id) %>
<%= product.product_name %><%= text_field_tag("product_shipments[][qty_shipped]")%>

Then your controller action should work as is.

cdesrosiers
  • 8,862
  • 2
  • 28
  • 33
  • Good news is the hash now looks good. The controller action needs adjustment. I posted the error at the end of my answer. – ctilley79 Jun 28 '12 at 23:46