ruby-on-railsruby-on-rails-3inheritancenested-formsmulti-table-inheritance

'Can't mass-assign protected attributes' when implementing Multiple Table Inheritance with nested forms


HI I am trying to implement the MTI in my application. I have a Person Model and 2 models inheriting from it: Client and TeamMember. When creating a Team Member I want to save to to database vallues for both person (first and last name, email etc) and team member(experience level, type of team, if lead or not). I am using the nested attributes form so in my team member form I am nesting the person fields. Unfortunatellly I am getting "Can't mass-assign protected attributes: person" error when trying to save. Can anyone tell me how this can be solved? Thanks! Models:

UPDATED TeamMember class but still the same error also tried people_attributes and persons_attributes and none of these worked

class TeamMember < ActiveRecord::Base
  has_many :project_team_members
  has_many :projects, through: :project_team_members
  has_one :person, as: :profile, dependent: :destroy
  accepts_nested_attributes_for :person
  attr_accessible :person_attributes, :experience_level, :lead, :qualification, :team
end

class Person < ActiveRecord::Base
  belongs_to :company
  belongs_to :profile, polymorphic: true
  attr_accessible :email, :first_name, :last_name, :phone_number, :profile_id,     :profile_type
end

Controller as follows:

class TeamMembersController < ApplicationController
  def create
    person = Person.create! { |p| p.profile = TeamMember.create!(params[:team_member]) }
    redirect_to root_url
  end

and the view:

<%= form_for(@team_member) do |f| %>
  <%= f.fields_for :person do |ff| %>
    <div>
      <%= ff.label :first_name %>
      <%= ff.text_field :first_name %>
    </div>
    <div>
      <%= ff.label :last_name %>
      <%= ff.text_field :last_name %>
    </div>
    <div>
      <%= ff.label :phone_number %>
      <%= ff.text_field :phone_number %>
    </div>
    <div>
      <%= ff.label :email %>
      <%= ff.text_field :email %>
    </div>
    <div>
      <%= ff.label :company_id %>
      <%= ff.text_field :company_id %>
    </div>
<% end %>
<div class="field">
  <%= f.label :team %><br />
  <%= f.text_field :team %>
</div>
<div class="field">
  <%= f.label :experience_level %><br />
  <%= f.text_field :experience_level %>
</div>
<div class="field">
  <%= f.label :qualification %><br />
  <%= f.text_field :qualification %>
</div>
<div class="field">
  <%= f.label :lead %><br />
  <%= f.check_box :lead %>
</div>
  <div class="actions">
<%= f.submit %>
</div>

UPDATED TeamMembersController (Solution thanks to the courtesy of Tiago)

def new
  @team_member = TeamMember.new
  @team_member.build_person

  respond_to do |format|
    format.html # new.html.erb
    format.json { render json: @team_member }
  end
end

def create
  @team_member = TeamMember.create!(params[:team_member])
  redirect_to root_url
end

Solution

  • To mass assign attributes in a nested form, you'll need to specify:

    class TeamMember < ActiveRecord::Base
      has_many :project_team_members
      has_many :projects, through: :project_team_members
      has_one :person, as: :profile, dependent: :destroy
      :experience_level, :lead, :qualification, :team #what is this line doing??
      accepts_nested_attributes_for :person      
      attr_accessible :person_attributes    
    end
    

    EDIT:

    In the action called before the form you need to build person. Like:

    @team_member = TeamMember.new
    @team_member.build_person
    

    Then you'll have one person (non-persisted) associated with @team_member.