我如何使用accepts_nested_attributes_for?

Tim*_* T. 5 ruby-on-rails has-many-through aggregation

编辑我的问题以简明扼要并更新我所做的事情:

如何为公司建立多个地址并为联系人分配单个地址,并在创建或编辑联系人时分配这些地址?

我想使用嵌套属性来创建新联系人时添加地址.该地址作为自己的模型存在,因为我可能希望选项从现有地址下拉而不是从头开始.

我似乎无法让它发挥作用.我得到一个未定义的方法`build'为nil:NilClass错误

这是我的联系人模型:

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
Run Code Online (Sandbox Code Playgroud)

这是我的地址模型:

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

  has_many :contacts
end
Run Code Online (Sandbox Code Playgroud)

我想在创建新联系人时,访问属于公司其他联系人的所有地址.以下是我代表公司的方式:

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

  has_many :contacts

  has_many :addresses, :through => :contacts

end
Run Code Online (Sandbox Code Playgroud)

以下是我在View for _form for Contact中创建字段的方法,以便当有人创建新的Contact时,他们将地址传递给Address模型并将该地址与Contact联系起来:

  <% f.fields_for :address, @contact.address do |builder| %>
  <p>
    <%= builder.label :street1, "Street 1" %> </br> 
    <%= builder.text_field :street1 %>
  <p>
  <% end %>
Run Code Online (Sandbox Code Playgroud)

当我尝试编辑时,Street 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
Run Code Online (Sandbox Code Playgroud)

这是我的错误控制台的片段: 我正在POST属性,但它不是在地址表中创建....

处理ContactsController #create(适用于2010-05-12 21:16:17的127.0.0.1)

[POST]参数:{"commit"=>"提交","authenticity_token"=>"d8/gx0zy0Vgg6ghfcbAYL0YtGjYIUC2b1aG + dDKjuSs =","contact"=> {"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("公司"."名称"='Allyforce')LIMIT 1 [0m

地址创建(16.0ms)[0m
[0; 1mINSERT INTO"地址"("city","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

联系创建(0.0ms)[0m
[0mINSERT INTO"contacts"("company","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

dom*_*esz 3

只是一个老生常谈的问题,如果您在联系表单中使用此内容不应该以单数形式表示?

 <% f.fields_for :address, @contact.address do |builder| %>
   <p>
     <%= builder.label :street1, "Street 1" %> </br> 
     <%= builder.text_field :street1 %>
   <p>
 <% end %>
Run Code Online (Sandbox Code Playgroud)

在你的行动中你还必须做

@ycontact.build_address
Run Code Online (Sandbox Code Playgroud)

如果您查看源代码,您的输入字段应该如下所示。

<输入类型=“文本”大小=“30”名称=“联系人[地址属性][street1]”id=“联系人地址属性_street1”>

我认为你的关联也有问题,如果你将 address_id 存储在 contact 中,那么

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

class Address < ActiveRecord::Base
  attr_accessible :street1
  has_many :contacts
end
Run Code Online (Sandbox Code Playgroud)

我认为这就是您的情况,因为您想将一个地址分配给多个联系人。

如果您将 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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您不能拥有具有相同地址的用户。

更新

在您的用户 show.html.eb 中您可以使用

<%= @contact.address.street1 %>
Run Code Online (Sandbox Code Playgroud)

在您公司的 show.html.erb 中,您可以显示如下地址:

<% @company.addresses.each do |address| %>
  <%= address.street1 %>
  ...
<% end %>
Run Code Online (Sandbox Code Playgroud)

在您的用户编辑页面上,表单字段应与 new.html.erb 中的表单字段相同,只是表单标记应如下所示:

<% form_for @contact do |form| %>
  <%=render :partial=>'fields' %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

部分字段包含您的所有字段:

 <% f.fields_for :address, @contact.address do |builder| %>
   <p>
     <%= builder.label :street1, "Street 1" %> </br> 
     <%= builder.text_field :street1 %>
   <p>
 <% end %>
Run Code Online (Sandbox Code Playgroud)

这应该有效。

更新2

您可以通过以下方式访问属于某个公司的所有地址:

@company.addresses
Run Code Online (Sandbox Code Playgroud)

如果你有

class Company<Activerecord::Base
    has_many :contacts
    has_many :addresses, :through=>:contacts
end
Run Code Online (Sandbox Code Playgroud)

关于草稿或默认地址。您可以根据您的需要有不同的选择。我希望联系人可以从数据库中选择现有地址之一。您可以在联系表单中选择一个选择框,例如:

 <%= form.select :address_id, options_from_collection_for_select(@company.addresses, 'id', 'street1')%>
Run Code Online (Sandbox Code Playgroud)

您还可以使用一些奇特的 JavaScript 来添加或删除地址表单,在这种情况下,如果用户选择现有地址之一。在这种情况下,您必须从表单中删除 address_attributes。我建议使用 javascript 代码创建一个链接,以添加和删除(切换样式)address_attributes。