2
votes

I have a form that is passing nested attributes it works for the 'Child' but the 'books' don't appear to be saving.

I have 3 models. Each Child can have 2 books:

class Child < ActiveRecord::Base
 has_many :hires
 has_many :books, through: :hires
end

class Hire < ActiveRecord::Base
 belongs_to :book
 belongs_to :child
 accepts_nested_attributes_for :book
 accepts_nested_attributes_for :child
end

class Book < ActiveRecord::Base
  has_many :hires
  has_many :children, through: :hires
  belongs_to :genres
end

The controller looks like this:

class HiresController < ApplicationController

...    

 def new
     @hire = Hire.new
     2.times { @hire.build_book }
 end

 def create
    @hire = Hire.new(hire_params)

    respond_to do |format|
      if @hire.save
        format.html { redirect_to @hire, notice: 'Hire was successfully created.' }
        format.json { render :show, status: :created, location: @hire }
      else
        format.html { render :new }
        format.json { render json: @hire.errors, status: :unprocessable_entity }
      end
    end
  end

 ...    

private
    # Use callbacks to share common setup or constraints between actions.
    def set_hire
      @hire = Hire.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
        def hire_params
  params.require(:hire).permit(:child_id, books_attributes: [ :id, :book_id, :_destroy])
end
end

The hash that is being submitted looks like this:

Processing by HiresController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"+2xxx==", "hire"=>{"child_id"=>"2", "books"=>{"book_id"=>"5"}}, "commit"=>"Create Hire"}
Unpermitted parameter: books

I can't work out why i'm getting the unpermitted params error? Any advice?

* EDIT - included the view as I now suspect this might play a part *

<%= form_for(@hire) do |f| %>

<%= f.select(:child_id, Child.all.collect {|a| [a.nickname, a.id]}) -%>
<%= f.label :child_id %><br>

<%= f.fields_for :books do |books_form| %>
<%= books_form.label :book_id %><br>
<%= books_form.select(:book_id, Book.all.collect {|a| [a.Title, a.id]}) -%>
<%= books_form.label :book_id %><br>
<%= books_form.select(:book_id, Book.all.collect {|a| [a.Title, a.id]}) -%>
<% end %>


<div class="actions">
<%= f.submit %>
</div>
<% end %>
1

1 Answers

0
votes

You need to use the following:

#app/controllers/hires_controller.rb
class HiresController < ApplicationController
   def new
      @hire = Hire.new
      2.times do
         @hire.build_book
      end
   end
end

The rest of it looks okay.

The problem you have is pretty typical; when you use f.fields_for, you have to build the associative objects, otherwise the fields_for won't create the correct field names.

The field names you're looking for will be [books_attributes][0][book_id] etc.


Since your form is passing all the attributes you'd expect (without the _attributes suffix), I can only surmise that your building of the associated objects is incorrect:

2.times { @hire.books.build }

... should be ...

2.times { @hire.build_book }

When you have singular associations, you have to build the associative object singularly: build_object, whilst with plural, you can use the plural.build call.