Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Nested Form Not Saving Nested Attributes

I have the following User controller:

class UsersController < ApplicationController

  def index
    @users = User.all
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    @customer = Customer.new

    if @user.save
      flash.notice = "User '#{@user.email}' was succefully created."
      redirect_to user_path(@user)
    else
      render 'new'
    end
  end

  def show
    @user = User.find(params[:id])
  end

  private

  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation, customer_attributes: [:id, :company])
  end
end

And I have the following User model:

class User < ActiveRecord::Base
  has_one :customer
  accepts_nested_attributes_for :customer, :allow_destroy => true
end

And the following Customer model:

class Customer < ActiveRecord::Base
  belongs_to :user
end

Finally, here is the form:

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

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

  <div class="field">
    <%= f.label :email %><br>
    <%= f.text_field :email %>
  </div>
  <div class="field">
    <%= f.label :password %><br>
    <%= f.password_field :password %>
  </div>
  <div class="field">
    <%= f.label :password_confirmation %><br>
    <%= f.password_field :password_confirmation %>
  </div>
  <%= f.fields_for :customers do |company| %>
    <div class="field">
      <%= company.label :company %><br>
      <%= company.text_field :company %>
    </div>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

When I submit the form, I see: `Unpermitted parameters: customers' in the log but it appears that I m indeed permitting it.

Also, I want to show the company name for each user in the show and index views. I'm not sure how to do that.

I remember using the build method in the past to get something similar to work but I can't seem to figure it out this time.

like image 990
calf Avatar asked Sep 06 '14 21:09

calf


2 Answers

Further to @Mandeep's answer, let me give you some further information:

  1. You need to "build" your associated objects for your form
  2. You need to process this as per the association your model has
  3. You need to save the attributes as per said association

The way to do this is relatively simple (outlined by Mandeep). However, the reason why might be a little less obvious:


Build

First, you need to build your associative association. This is vitally important, primarily because Rails (by virtue of being built on Ruby), is an object orientated framework.

Object orientation, without getting into too much detail, means that everything you do with Rails is going to be based around objects. In the case of our beloved Rails, it means that every Model is an object.

By virtue of this fact, the nested model paradigm has to be built in Rails whenever you want to create such a form. To do this, you need to use the build methods - which tell ActiveRecord (Rails' object relational mapper) that you have another associated model / object which you want to populate:

#app/controllers/users_controller.rb
class UsersController < ApplicationController
   def new
      @user = User.new #-> initializes "User" object
      @user.build_customer #-> "builds" the associated object
   end
end

This gives Rails a set of associated data which it can populate with your form (considering you call the correct methods)

--

Association

Second, you need to consider the association you have. This is important as singular & multiple associations are handled differently in the "build" process.

You're using a has_one relationship, which means you need to use singular association names (although you can call the associations whatever you want):

enter image description here

If you used a has_many association, you'd need to use the plural association methods:

enter image description here

This explains the need to use the build_customer method; but also should give you the presidence to use the singular association name for all the methods you need to get this working, namely fields_for and params:

#app/views/users/new.html.erb
<%= form_for @user do |f| %>
  ...
  <%= f.fields_for :customer do |c| %>
      ...
  <% end %>

  <%= f.submit %>
<% end %>

#app/controllers/users_controller.rb
class UsersController < ApplicationController
   def create
      @user = User.new user_params
      @user.save
   end

   private

   def user_params
      params.permit(:user).permit(:user, :params, customer_attributes: [:x. :y])
   end

end

--

Save

The above controller code will save the attributes you require.

You must understand that passing nested attributes means that the model you're sending the associative data to needs to be subordinated to your "main" model. This happens with the ActiveRecord associations in your models, as discussed initially.

Hopefully this gives you some more clarity

like image 118
Richard Peck Avatar answered Nov 04 '22 01:11

Richard Peck


Change your code to this:

def new
  @user = User.new
  @user.build_customer
end

your form:

<%= form_for @user do |f| %>
  // user fields
  <%= f.fields_for :customer do |customer| %> 
    // customer fields
  <% end %>
<% end %>   

Also there is not need of @customer = Customer.new in your create method.

like image 4
Mandeep Avatar answered Nov 04 '22 02:11

Mandeep