How to use accepts_nested_attributes_for?

Editing my question for brevity and updating what I did:

How can I model several addresses for a company and assign one address to a contact and be able to assign them when creating or editing a contact?

I want to use nested attributes in order to be able to add an address while creating a new contact. This address exists as its own model, because I may need the ability to drop out of existing addresses instead of typing from scratch.

I can not make it work. I get an undefined `build 'method for nil: NilClass error

Here is my contact model:

class Contact < ActiveRecord::Base
  attr_accessible :first_name, :last_name, :title, :phone, :fax, :email, :company, 
                  :date_entered, :campaign_id, :company_name, :address_id, :address_attributes

  belongs_to :company
  belongs_to :address
  accepts_nested_attributes_for :address
end

Here is my model for the address:

class Address < ActiveRecord::Base
  attr_accessible :street1, :street2, :city, :state, :zip

  has_many :contacts
end

, , , , . , :

class Company < ActiveRecord::Base
  attr_accessible :name, :phone, :addresses

  has_many :contacts

  has_many :addresses, :through => :contacts

end

_ , , - , :

  <% f.fields_for :address, @contact.address do |builder| %>
  <p>
    <%= builder.label :street1, "Street 1" %> </br> 
    <%= builder.text_field :street1 %>
  <p>
  <% end %>

, 1 . , show.html.erb.

- :

:

  def new
    @contact = Contact.new
    @contact.address.build # Iundefined method `build' for nil:NilClass

    @contact.date_entered = Date.today
    @campaigns = Campaign.find(:all, :order => "name")
    if params[:campaign_id].blank? 

    else
      @campaign = Campaign.find(params[:campaign_id])
      @contact.campaign_id = @campaign.id
    end

    if params[:company_id].blank?

    else
      @company = Company.find(params[:company_id])
      @contact.company_name = @company.name
    end

  end

  def create
    @contact = Contact.new(params[:contact])
    if @contact.save
      flash[:notice] = "Successfully created contact."
      redirect_to @contact
    else
      render :action => 'new'
    end
  end

  def edit
    @contact = Contact.find(params[:id])
    @campaigns = Campaign.find(:all, :order => "name")
  end

: POSTING , ....

Controller # create ( 127.0.0.1 2010-05-12 21:16:17)

[POST] : { "" = > "", "Authenticity_token" = > "D8/gx0zy0Vgg6ghfcbAYL0YtGjYIUC2b1aG + dDKjuSs =", "" = > { "company_name" = > "Allyforce", "title" = > "," campaign_id "= > " 2 "," Address_attributes "= > {" Street1 "= > " ABC "}," fax" = > "," phone" = > "," Last_name" = > "," Date_entered "= > " 2010-05-12 "," email" = > "," first_name "= > " abc "}}

(0.0ms) [0m [0mSELECT * FROM" "WHERE (" "." Name" = "Allyforce" ) LIMIT 1 [0m

(16.0ms) [0m
[0; 1MINSERT INTO " "(" "," zip "," created_at "," street1 "," updated_at "," street2 "," state ") VALUES (NULL, NULL, '2010-05-13 04:16:18 ', NULL,' 2010-05-13 04:16:18 ', NULL, NULL) [0m

Create (0.0ms) [0m
[0mINSERT INTO" "(" "," created_at", "title", "updated_at "," campaign_id "," address_id "," last_name "," phone "," fax "," company_id "," date_entered "," first_name "," email") VALUES (NULL, '2010-05-13 04:16:18', '', '2010-05-13 04:16:18 ', 2, 2,' ',' ',' ', 5, '2010-05-12', 'abc', '') [0m

+5
4

, , ?

 <% f.fields_for :address, @contact.address do |builder| %>
   <p>
     <%= builder.label :street1, "Street 1" %> </br> 
     <%= builder.text_field :street1 %>
   <p>
 <% end %>

@ycontact.build_address

, .

< input type = "text" size = "30" name= " [address_attributes] [Street1]" ID = "contact_address_attributes_street1" >

, , _ ,

class Contact < ActiveRecord::Base
  belongs_to :address
  accepts_nested_attributes_for :address
end

class Address < ActiveRecord::Base
  attr_accessible :street1
  has_many :contacts
end

, , .

, contact_id :

class Contact < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
end

class Address < ActiveRecord::Base
  attr_accessible :street1
  belongs_to :contact
end

.

show.html.eb

<%= @contact.address.street1 %>

show.html.erb , :

<% @company.addresses.each do |address| %>
  <%= address.street1 %>
  ...
<% end %>

, new.html.erb, :

<% form_for @contact do |form| %>
  <%=render :partial=>'fields' %>
<% end %>

togheter :

 <% f.fields_for :address, @contact.address do |builder| %>
   <p>
     <%= builder.label :street1, "Street 1" %> </br> 
     <%= builder.text_field :street1 %>
   <p>
 <% end %>

.

2

, , :

@company.addresses

class Company<Activerecord::Base
    has_many :contacts
    has_many :addresses, :through=>:contacts
end

. . , . , :

 <%= form.select :address_id, options_from_collection_for_select(@company.addresses, 'id', 'street1')%>

- javascript , , . address_attributes . javascript-, address_attributes ( ).

+3

(, ), :

class Company < ActiveRecord::Base
  has_many :contacts
end

class Contact < ActiveRecord::Base
  belongs_to :company
  has_one :address
end

class Address < ActiveRecord::Base
  belongs_to :contact
end

, Address.

(.. , ):

class Company < ActiveRecord::Base
  has_many :contacts
end

class Contact < ActiveRecord::Base
  belongs_to :company
  belongs_to :address
end

class Address < ActiveRecord::Base
  has_many :contacts
end

has_many :through, company.addresses:

class Company < ActiveRecord::Base
  has_many :contacts
  has_many :addresses, :through => :contacts
end

(, , ..) .

- , . AJAXified , .

/ , . Railscast 196 - .

EDIT -

accepts_nested_attributes_for has_many has_one . , , , , :

class Company < ActiveRecord::Base
  has_many :contacts
  accepts_nested_attributes_for :contact
end

class Contact < ActiveRecord::Base
  belongs_to :company
  has_one :address # or has_many, if you prefer
end

class Address < ActiveRecord::Base
  belongs_to :contact
end

form_for fields_for, Railscast, . , . ryanb , .

+1

, , , . ( ), "_" " ". "has_many: ", "has_many: ".

, , , : addressable_type : addressable_id .

class Company < ActiveRecord::Base
  attr_accessible :name, :phone, :addresses

  has_many :contacts  
  accepts_nested_attributes_for :contacts

  has_many :addresses, :as => :addressable
  accepts_nested_attributes_for :addresses

end

class Contact < ActiveRecord::Base
  attr_accessible :first_name, :last_name, :title, :phone, :fax, :email, :company, 
                  :date_entered, :campaign_id, :company_name

  belongs_to :company
  has_many :addresses, :as => :addressable
  accepts_nested_attributes_for :addresses
end

class Address < ActiveRecord::Base
  attr_accessible :street1

  belongs_to :addressable, :polymorphic => true
end

"company.addresses", , . .

, :

<% form_for @company do |company_form| %>
  <% company_form.fields_for :addresses do |address_form| %>
    <%= address_form.text_field :street1 %>
  <% end %>
  <% company_form.fields_for :contacts do |contact_form| %>
    <% contact_form.fields_for :addresses do |contact_address_form| %>
      <%= contact_address_form.text_field :street1 %>
    <% end %>
  <% end %>
<% end %>

:

class CreateAddresses < ActiveRecord::Migration
  def self.up
    create_table :addresses do |t|
      t.string :street1
      t.string :addressable_type
      t.integer :addressable_id
      t.timestamps
    end
  end

  def self.down
    drop_table :addresses
  end
end

, , .

, , , .

!

+1

I would recommend that a contact have many addresses. One way or another, in the wild. If a decision is made for only one, it will behas_one :address

Oh, and you must circle them: there has_many :Contactsmust behas_many :Contacts

In the Address model, you reference (the addressable_idand field is required for the database addressable_type)

belongs_to :addressable, :polymorphic => true
0
source

All Articles