0

I am trying to make a nested form that can be edited then saved. Whenever I go to save the form though I get the error:

Couldn't find ClassInstance with ID=11 for Collection with ID=

11 is the ClassInstance id for the first ClassInstance in the form, and it appears that the Collection id is being set to NULL.

I have tried to manually set hidden ids in the controller form_for as well as setting a hidden field in _class_instance_fields.html.erb for the :collection_id.

What is ActiveRecord trying to do and why is it getting confused? Am I doing something you aren't supposed to with nested forms?

I have included some of my code with the fluff removed so that it isn't as much to look through.

collections_controller.rb

 def update
    @collection = Collection.new(params[:collection])

    if @collection.save
      flash[:notice] = "IT WORKED"
    else
      flash[:notice] = "No Cigar...."
    end
    redirect_to collection_path
  end

  def read
      @collection = Collection.find_by_user_id(current_user.id)
      @classes = Classification.all 
  end

read.html.erb

<%= form_for @collection, url: {:action => "update", collection_id: @collection.id} do  |f| %>
        <%= f.hidden_field :id #I had hoped this would fix it%>
        <table class="instance_table">
                <%= f.fields_for :class_instances do |builder| %>
                    <%= render 'shared/class_instance_fields', :f => builder, :status => '0' %>
                <% end %>
                <tr><td>
                    <%= link_to_add_fields "Add an Instance", f, :class_instances  %>
                </td></tr>
        </table>
        <%= f.submit "Save"%>
    <% end %>

_class_instance_fields.html.erb

<tr class="record_row">
    <td>
        <%=  f.label( :name) %></p>
    </td>
        <%= f.hidden_field :status, :value => status  %>
        <%= f.hidden_field :collection_id %>
    <td>
        <% if status == '1'  %>
            <%= f.text_field :name %>
        <% else %>
            <%= link_to f.object.name, instance_edit_path(f.object.id) %>
        <% end %>
    </td>
    <td>
        <% if status == '1' %>
            <%=  f.select :classification, options_for_select(@classes.collect{|c| [c.name, c.id]},selected:  f.object.class_id)%>
        <% else %>
            <%= f.label :classification,  displayClassInstanceName(@classes.select{|a|  a[:id] == f.object.class_id }[0])  %>
        <% end %>
    </td>
    <td>
        <% user = nil%>
        <% id = nil %>
        <% if status == '1' %>
            <%collection = current_user.id %>
        <% else %>
            <%collection = f.object.collection_id %>
        <% end %>
        <%= f.label :username, displayUserName( user ) %>

    </td>
    <td>
        <%= removeRecordLink(f) %>
    </td>

</tr>

Additional Info

I have been using Rails.logger.debug to inspect the params before I save them. With the folling code in the `update

Rails.logger.debug "TG: " << params.to_yaml

I get the following output

TG: --- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: ✓
_method: put
authenticity_token: efvqGsLKJfdCsadfjsad9fsD97dfgYaOUjsg+uvAQi2+k4=
collection: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
  name: mikee's Collection
  class_instances_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
    '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
      status: '0'
      collection_id: '8'
      _destroy: '0'
      instance_attributes_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
        '0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
          id: '1'
          _destroy: '0'
      id: '11'
commit: Save
controller: collections
action: update
id: '8'

Maybe that can help make sense of things, as I am not too experience with params and I'm not sure what I should be seeing there for nested forms.

CoderDake
  • 1,497
  • 2
  • 15
  • 30

3 Answers3

1

I wanted to comment on your post but don't have 50 reputation yet... so I'll just post here. Hope you'll find it helpful.

Have you tried <%= @collection.inspect %> before your form to see what data Rails is dropping into the form? You shouldn't have to specify the id or collection_id anywhere, since form_for takes care of that for you.

I would comment out the rest of the fields in your form and then do a render text: params.inspect and return in your update action to see exactly what data is being sent to it via PATCH (assuming you're running Rails 4).

If the id (or collection_id depending on your route) shows up properly, then you can start uncommenting parts of the form... rinse and repeat until you find the offending code.

Edit: I agree with Philip7899. This appears to be a strong parameter issue.

Vilmos Csizmadia
  • 316
  • 1
  • 2
  • 11
  • Unfortunately I have already been doing something similar. I have been using `Rails.logger.debug` to check what the form is passing to the controller. I feel like I've done a lot of rinsing and lathering with the fields I supply. I'm going to update my question with the results of the output that I get though, maybe that could shed some light, as I'm not too sure what it is supposed to look like – CoderDake Nov 26 '13 at 02:42
1

In contrast to the others, I do not believe this is a strong parameters issue. Strong parameters only kicks in when you say something like params.require(:collection).

I'm having a hard time understanding what you are trying to do here. Your method is called update, which indicates that it updates an existing record, but instead it creates a new one (Collection.new). I will assume you are trying to update an existing record.

There are a few problem-areas here. First, the controller. Replace this:

@collection = Collection.new(params[:collection])

with this:

@collection = Collection.find(params[:id])
@collection.assign_attributes(params[:collection])

Then the view...

When a form updates a resource in Rails, the standard procedure is to include the object ID in the form action URL. That happens automatically when you use form_for @collection. Since you are overriding the form's url with url: {:action => "update", collection_id: @collection.id}, it is not generating the path correctly. Change your form_for line to:

<%= form_for @collection do |f| %>

and remove f.hidden_field :id

For this to work correctly, you must have the appropriate route set up in routes.rb. I'm assuming you have a line in there like resources :collections.

I can't tell what you're trying to do with class_instances, but if you're hoping for additional objects to be created when you save your collection, you may need to set accepts_nested_attributes_for on your Collection model. Read about that here:

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Once this is working, you may want to replace params[:collection] with collection_params, and then you should add a method to your controller

protected
def collection_params
  params.require(:collection).permit(:param_1, :param_2, :param_3)
end

Where :param_1, :param_2, :param_3 are the params you wish to allow the user to update.

See https://github.com/rails/strong_parameters and Rails 4 - Strong Parameters - Nested Objects

Community
  • 1
  • 1
Nick Urban
  • 3,568
  • 2
  • 22
  • 36
  • Thanks for the help in understanding what I was messing up with my form. That had actually been bugging me for a while! I thought it was really weird that I was using new to update the Models and I kicked myself when I realized that I was looking at the wrong action in the example when I made my controller. – CoderDake Nov 27 '13 at 23:36
  • Yes I did, trying to use new was foolish, using assign_attributes fixed me up right! It was frustrating and relieving to find out that I missed such a tiny detail :P Thanks again! On to the next problem :P – CoderDake Nov 28 '13 at 00:25
0

If you are using rails 4, add :id to your strong params for class_instances. If you're using rails 3, make the changes to your model so that the user can set them.

Philip7899
  • 4,599
  • 4
  • 55
  • 114