Nested form is giving me the wrong params

I am working on an invoice tracking system for my business and have gotten so close to getting this portion to work!

I have an Invoice model with many InvoiceItems.

I am following this example (which is based off of ryan daigle's post) http://transfs.com/devblog/2009/06/26/nested-forms-with-rails-2-3-helpers-and-javascript-tricks/ and I've looked at a couple of other similar examples as well.

I can generate the fields just fine, but when I try to add more fields and save, I get the following message:

ActiveRecord::UnknownAttributeError in InvoicesController#update

unknown attribute: invoice_items

Parameters: {"commit"=>"Submit", "invoice"=>{"invoice_items_attributes"=>{"1248641854778"=> {"invoice_items"=>{"project_id"=>"9", "_delete"=>"0", "amount_billed"=>"48"}}, "0"=>{"project_id"=>"8", "_delete"=>"0", "id"=>"29", "amount_billed"=>"32.00"}}, "date_due(1i)"=>"2009", "date_due(2i)"=>"8", "date_due(3i)"=>"9"}, "client_id"=>"1", "_method"=>"put", "authenticity_token"=>"WiwqytxYy/KZSrO76kWKGCH2hMlgiCj1lSedQV3h6KA=", "id"=>"17"}

The second line of parameters looks like it goes one level too deep with the Hash key "invoice_items". If you go further down to where the project_id is 8, that is the record that was already in existance. Below is the yaml output of the request.

Here is the same request converted to yaml:

--- !map:HashWithIndifferentAccess commit: Submit invoice: !map:HashWithIndifferentAccess   invoice_items_attributes: !map:HashWithIndifferentAccess     "1248641854778": !map:HashWithIndifferentAccess       invoice_items: !map:HashWithIndifferentAccess         project_id: "9"         _delete: "0"         amount_billed: "48"     "0": !map:HashWithIndifferentAccess       project_id: "8"       _delete: "0"       id: "29"       amount_billed: "32.00"   date_due(1i): "2009"   date_due(2i): "8"   date_due(3i): "9" authenticity_token: WiwqytxYy/KZSrO76kWKGCH2hMlgiCj1lSedQV3h6KA= _method: put client_id: "1" action: update id: "17" controller: invoices

My form basically looks like this:

<% form_for [@client, @invoice], setup_invoice(@invoice) do |f| %>   <%= f.error_messages %>

<h2>Add Projects:</h2>   <ul id="invoice_items">     <li class="invoice_item">

      <% f.fields_for :invoice_items do |invoice_item| %>         <%= invoice_item.collection_select :project_id, @client.projects, :id, :title, { :include_blank => true } %>         <%= invoice_item.label :amount_billed, "Amount to bill" %>         <%= invoice_item.text_field :amount_billed %>

        <% if invoice_item.object.new_record? %>           <%= link_to_function "clear", "this.up('.invoice_item').remove ()" %>         <% else %>           <%= invoice_item.check_box '_delete' %>           <%= invoice_item.label '_delete', 'Remove' %>         <% end %>       <% end %>

    </li>   </ul>   <%= link_to_new_nested_form "Add a Project", f, :invoice_items %>   <hr />   <p>     <%= f.label :date_due %><br />     <%= f.date_select :date_due, :default => 15.days.from_now,                                   :order => [:day, :month, :year],                                   :start_year => Time.now.year

                                   %>   </p>   <p><%= f.submit "Submit" %></p> <% end %>

I'm just not sure how to change the code to make it work!

Here is what I have in my application_helper.rb:

   def generate_html(form_builder, method, options = {})      options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new      options[:partial] ||= method.to_s.singularize      options[:form_builder_local] ||= :f      form_builder.fields_for(method, options[:object], :child_index => 'NEW_RECORD') do |f|        render(:partial => options[:partial], :locals => { options [:form_builder_local] => f })      end    end

   def link_to_new_nested_form(name, form_builder, method, options = {})      options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new      options[:partial] ||= method.to_s.singularize      options[:form_builder_local] ||= :f      options[:element_id] ||= method.to_s      options[:position] ||= :bottom      link_to_function name do |page|        html = generate_html(form_builder,                            method,                            :object => options[:object],                            :partial => options[:partial],                            :form_builder_local => options [:form_builder_local]                           )              page << %{                $('#{options[:element_id]}').insert({ #{options [:position]}: "#{ escape_javascript html }".replace(/NEW_RECORD/g, new Date().getTime()) });              }      end    end

I've had other problems with this approach as well, but if I can at least get it to save to the db I should be okay.

Three things I can offer advice on here. You seem to be like me in you post a lot of information..

So, first post all that code on pastie.org or gisthub so it's ordered nicely and easy to view. Then, post the link back here.

Second, you ran a <%= debug(params) %> in your view to verify all parameters?

Third, your field does exist in the table you are querying?

If all those checks out, I'll see if i can see anything on your new view code. Four eyes are better than 2. :slight_smile:

First of all, thanks for taking the time to recommend a better way of displaying my problem!

Here is the gist http://gist.github.com/155971

The problem arises when I use the helper method in my application_helper.rb to create new fields for the nested form.

In my controller I build one instance of the invoice_item (@invoice.invoice_items.build) and that field works just fine, when viewing the parameters, it is the section that reads:

"0": !map:HashWithIndifferentAccess       project_id: "8"       _delete: "0"       id: "31"       amount_billed: "32.00"

It is the one above (in the params from the gist) that that looks similar (project_id: 9, amount_billed: "68") that is the problem, it is getting nested too deep under "invoice_items"…unnecessarily so it would seem. I have added my Invoice and InvoiceItems models to the gist as well.

Thanks for your help!

Hi Matt,

Well the code checks out fine from my end with regards to syntax. However, I have a question in regards to your invoice..

Are you saving anything at all before the new nested instance is created? The reason I ask is because you are using :child_index and I was just curious whether you would run into issues with unused instances, or perhaps the issue is somewhere in that piece?

Other than that, I just don't know mate..

I'm not saving anything before the new nested instance is created. I didn't write the piece using :child_index, but perhaps I should do some more research about that chunk of code. Thanks again for your help, I'll try to post the solution when I find it!

Yet another stupid mistake. I wasn't keeping track of my partials and I had nested my nested form, thus creating the extra level of params. Sheesh