Search code examples
ruby-on-railsstrong-parameters

Rails 4 nested strong parameters throwing unpermited parameters error


I've been at this for hours now but can't figure it out:

Trying to create a new address in the new organisation form. An address can be created for an organisation, company or employee. I want the Address_id relevant to the Orgainsation/Company/Employee to be stored in their database record but can't get strong_parameters to work properly.

models/organisation.rb

class Organisation < ActiveRecord::Base
  belongs_to :address
  has_many :users
  accepts_nested_attributes_for :address
end

models/address.rb

class Address < ActiveRecord::Base
    has_many :organisations
    has_many :companies
    has_many :employees
end

views/organisations/_form.html.erb

<%= form_for(@organisation) do |f| %>
  <% if @organisation.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@organisation.errors.count, "error") %> prohibited this organisation from being saved:</h2>

  <ul>
  <% @organisation.errors.full_messages.each do |message| %>
    <li><%= message %></li>
  <% end %>
  </ul>
 </div>
<% end %>

<div class="field">
  <%= f.label :name %><br>
  <%= f.text_field :name %>
</div>

<%= f.fields_for @address do |t| %> 
  <div>
    <%= t.label :Line_1 %><br />
    <%= t.text_field :line1 %>
  </div>
  <div>
    <%= t.label :Line_2 %><br />
    <%= t.text_field :line2 %>
  </div>
  <div>
    <%= t.label :Line_3 %><br />
    <%= t.text_field :line3 %>
  </div>
  <div>
    <%= t.label :Line_4 %><br />
    <%= t.text_field :line4 %>
  </div>
  <div>
    <%= t.label :Postcode %><br />
    <%= t.text_field :postcode %>
  </div>     
  <div>
    <%= t.label :Country %><br />
    <%= t.text_field :country %>
  </div>                            
<% end %>

 <div class="field">
 <%= f.label :phone %><br>
 <%= f.text_field :phone %>
 </div>

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

controllers/organisations_controller.rb

class OrganisationsController < ApplicationController
 espond_to :html, :json
 before_action :set_organisation, only: [:show, :edit, :update, :destroy]

 def new
  @organisation = Organisation.new
  @address = Address.new
  respond_with(@organisation)
 end


 def create
  puts "Start of debug"
  puts organisation_params[:address_attributes]
  puts "End of debug"
  @organisation = Organisation.new(organisation_params)
  @address = Address.new(organisation_params[:address_attributes])
  @address.save
  @organisation.address = @address.id
  @organisation.save
  respond_with(@organisation)
 end

 private
 def set_organisation
  @organisation = Organisation.find(params[:id])
 end

 def organisation_params
   params.require(:organisation).permit(:name, :phone, :creator, :contact, :renewal, :approved, :balance, address_attributes:[:line1, :line2, :line3, :line4, :postcode, :country, :organisation_id])
 end
end

But I keep getting the following error:

Started POST "/organisations" for 127.0.0.1 at 2014-12-11 23:40:54 +0000
Processing by OrganisationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"T2344536dhfjkhkj3eb5g43glwy547fy5p2nbydddfr=", "organisation"=>{"name"=>"The company", "address"=>{"line1"=>"1 The Road", "line2"=>"", "line3"=>"", "line4"=>"", "postcode"=>"", "country"=>"Ireland"}, "phone"=>"01111111"}, "commit"=>"Create Organisation"}
Start of debug
Unpermitted parameters: address

End of debug
Unpermitted parameters: address
Unpermitted parameters: address
   (0.4ms)  BEGIN
  SQL (1.0ms)  INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2014-12-11 23:40:54.404236"], ["updated_at", "2014-12-11 23:40:54.404236"]]
PG::NotNullViolation: ERROR:  null value in column "line1" violates not-null constraint
DETAIL:  Failing row contains (21, null, null, null, null, null, null, 2014-12-11 23:40:54.404236, 2014-12-11 23:40:54.404236).
: INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"
   (0.4ms)  ROLLBACK
Completed 500 Internal Server Error in 7ms

ActiveRecord::StatementInvalid (PG::NotNullViolation: ERROR:  null value in column "line1" violates not-null constraint
DETAIL:  Failing row contains (21, null, null, null, null, null, null, 2014-12-11 23:40:54.404236, 2014-12-11 23:40:54.404236).
: INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"):
  app/controllers/organisations_controller.rb:29:in `create'

It looks to me that the address is not getting created because it doesn't have access to the address attributes which are passed but are 'unpermitted parameters'. Even though I include it in the organisation_params.permit statement????

Am I doing something wrong here?

Edit 1

As per hkumar's suggestion I've edited the views/organisations/_form.html.erb file to now say <%= f.fields_for "#{@address}_attributes" do |t| %>

But the error now looks like:

 Parameters: {"utf8"=>"✓", "authenticity_token"=>"T2344536dhfjkhkj3eb5g43glwy547fy5p2nbydddfr=", "organisation"=>{"name"=>"The company", "#<Address:0x00000003aa2690>_attributes"=>{"line1"=>"The Road", "line2"=>"", "line3"=>"", "line4"=>"", "postcode"=>"", "country"=>"Ireland"}, "phone"=>"01111111"}, "commit"=>"Create Organisation"}
Start of debug
Unpermitted parameters: #<Address:0x00000003aa2690>_attributes

End of debug

Edit 2

Followed Paul Richter's advice and now the error is:

Started POST "/organisations" for 127.0.0.1 at 2014-12-12 01:35:13 +0000
Processing by OrganisationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"T2344536dhfjkhkj3eb5g43glwy547fy5p2nbydddfr=", "organisation"=>{"name"=>"The company", "address_attributes"=>{"line1"=>"The road", "line2"=>"", "line3"=>"", "line4"=>"", "postcode"=>"", "country"=>"Ireland"}, "phone"=>"01111111"}, "commit"=>"Create Organisation"}
Start of debug
{"name"=>"The company", "phone"=>"01111111", "address_attributes"=>{"line1"=>"The road", "line2"=>"", "line3"=>"", "line4"=>"", "postcode"=>"", "country"=>"Ireland"}}
End of debug
   (0.6ms)  BEGIN
  SQL (0.9ms)  INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2014-12-12 01:35:14.196724"], ["updated_at", "2014-12-12 01:35:14.196724"]]
PG::NotNullViolation: ERROR:  null value in column "line1" violates not-null constraint
DETAIL:  Failing row contains (46, null, null, null, null, null, null, 2014-12-12 01:35:14.196724, 2014-12-12 01:35:14.196724).
: INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"
   (0.3ms)  ROLLBACK
Completed 500 Internal Server Error in 57ms

ActiveRecord::StatementInvalid (PG::NotNullViolation: ERROR:  null value in column "line1" violates not-null constraint
DETAIL:  Failing row contains (46, null, null, null, null, null, null, 2014-12-12 01:35:14.196724, 2014-12-12 01:35:14.196724).
: INSERT INTO "addresses" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"):
  app/controllers/organisations_controller.rb:29:in `create'

Final edit

Thanks to Paul Richter, the following code now works (for anyone who stumbles across it):

controllers/organisations_controller.rb

class OrganisationsController < ApplicationController
 espond_to :html, :json
 before_action :set_organisation, only: [:show, :edit, :update, :destroy]

 def new
    @organisation = Organisation.new
    @organisation.build_address
 end


 def create
  @organisation = Organisation.new(organisation_params)
  @organisation.save
  respond_with(@organisation)
 end

 private
 def set_organisation
  @organisation = Organisation.find(params[:id])
 end

 def organisation_params
   params.require(:organisation).permit(:name, :phone, :creator, :contact, :renewal, :approved, :balance, address_attributes:[:line1, :line2, :line3, :line4, :postcode, :country, :organisation_id])
 end
end

Solution

  • The issue relates to this line in the form:

    f.fields_for @address do |t|
    

    And this line in the controller (showing the full method so we are all on the same page):

    def new
      @organisation = Organisation.new
      @address = Address.new  # This line, specifically
      respond_with(@organisation)
    end
    

    The problem is that you're creating separate Address and Organization objects as though they are separate independent entities, but since Address belongs_to Organization (according to your model), Address is actually dependent on the parent Organization, and therefore should be associated properly.

    Instead, you would want to do this:

    def new
      @organisation = Organisation.new
      @organization.build_address
     ...
    

    This will set up a new Address object which is associated with its parent Organization.

    Then in the form, change the fields_for line to this:

    f.fields_for :address do |t|
    

    This will call the address method of the @organization object, and [insert rails magic] the naming convention for accepts_nested_attributes_for will be used instead.

    So in short, the reason you were seeing the error is because, while you had indeed set up the strong parameters correctly, the fact that a separate, disconnected Address object was being passed to f.fields_for prevented the form fields from being named correctly.

    Edit

    Now that the form is set up properly, change the create method to look like this:

    def create
      @organisation = Organisation.new(organisation_params)
      @organisation.save
      respond_with(@organisation)
    end
    

    In the comments below, you were mentioning how to get the id of the address I'm creating and store it on the organisation. To be clear, there is no id of the address at this point, but because you've set up the accepts_nested_attributes_for :address line in the Organization model, Rails will create a new Address object already associated to the Organization by using the address_attributes parameters in the organization_params hash you passed in.

    This one line (Organisation.new(organisation_params)) creates the whole structure for you, so long as the parameters match the expected naming convention, which they now should.