2010-05-09 24 views
5

编辑我为简洁的问题,更新自己的所作所为:如何使用accep_nested_attributes_for?

如何建模具有对公司多个地址,并分配一个地址到联系人,并能创建时为它们分配或编辑联系人?

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

我似乎无法得到它的工作。我得到了零一个未定义的方法`编译”: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 

这里是我的地址模型:

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 

这里是我正在尝试在信息查看_form的联系创造一个字段,这样,当有人创建了一个新的联系,他们通过地址到地址模型并将该地址与联系人关联:

<% 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 

这里是一个片段我错误控制台: 我张贴的属性,但它不能在地址表制作....

处理ContactsController#CR eate (为127.0.0.1在2010-05-12 21点16分十七秒)

[POST]参数: { “提交”=> “提交”, “authenticity_token”=>“D8/gx0zy0Vgg6ghfcbAYL0YtGjYIUC2b1aG + “=”, “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“companies”WHERE(“companies”。“name”='Allyforce') LIMIT 1 [0m

Address Create(16。0ms)[0; 0; 0; 0; 0)[0; 0; 0; 0; 0)[0; 1mINSERT INTO“addresses” VALUES(NULL, NULL,'2010-05-13 04:16:18',NULL,'2010-05-13 04:16:18',NULL,NULL)[0m

Contact Create(0.0ms)[0m
[0mINSERT INTO “联系人”( “公司”, “created_at”, “标题”, “的updated_at”, “CAMPAIGN_ID”, “ADDRESS_ID”, “姓氏”, “手机”, “传真”, “ company_id“,”date_entered“, ”first_name“,”email“)VALUES(NULL, '2010-05-13 04:16:18','','2010-05-13 04:16:18',2,'','','',5, '2010- 05-12' , 'ABC', '')[0米

+0

嗯我觉得在地址表中创建insers值,这里是 地址创建(16.0ms)[0; 1mINSERT INTO“addresses”(“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 – dombesz 2010-05-13 07:02:04

+0

更新我的帖子,包含视图中的代码。 – dombesz 2010-05-13 07:30:48

+0

你可以看到,在地址创建street1的值是空白的...不是“ABC”.... – Angela 2010-05-14 00:13:14

回答

3

只是一个平庸的问题,如果你在你的联系表单中使用这个问题不应该是单数地址?

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

在你的行动,你必须也做

@ycontact.build_address 

如果你看一下源代码的输入框的样子。

<输入类型= “文字” 大小= “30” NAME = “接触[address_attributes] [street1]” ID = “contact_address_attributes_street1”>

我想你也有问题你的协会,如果你存储address_id在联系,然后

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 %> 

局部领域包含了所有你fieds 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并删除(切换样式)。

+0

那么,我想让用户拥有相同的地址。但是可能有多个地址......想象一个拥有不同员工的大型分布式公司。所以这项工作....这是使用:通过=>:联系人,对不对? – Angela 2010-05-13 01:08:38

+0

也... accept_nested_attributes_for:地址工作联系方式belongs_to:地址?出现我得到一个错误....我认为它只适用于has_many或has_one ....我更新了我的问题,以简化问题 – Angela 2010-05-13 04:13:17

+0

accepted_nested_attributes_for也与belongs_to一起使用,我现在正在使用它。直到你想通过公司模型创建地址,has_many:through并不重要。也没有真正的意义。 – dombesz 2010-05-13 07:03:58

1

如果每个地址只能属于一个联系人(如家庭地址),你可以这样做:

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 

在这两个地址组件(街道,城市,州等)是地址表中的列。

你问题的最后一部分确实取决于数据分布 - 如果你只有几个地址,你可以简单地将它们粘贴在主地址为默认的select元素中。有了更多的地址,你可能需要一个带自动完成的AJAX化表单来搜索地址表。

要获得新的联系人/地址到表中,您可能需要查看嵌套表单。 Railscast 196是一个很好的介绍。

编辑 - 更多关于嵌套属性

accepts_nested_attributes_for应该无论是has_manyhas_one ASSOCATION工作,并应与多层嵌套工作。看来你想有一个联系人属于公司和地址属于一个联系,在这种情况下:

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_forfields_for如Railscast描述得到嵌套的表单元素。如果你的表单不寻常,这可能有点棘手。 ryanb here有很多典型的例子。

+0

你好!这与Rails Recipes中描述的使用多态的方式有何不同?每个地址实际上可能有多个联系人;所以每个公司都有多个地址....谢谢。 – Angela 2010-05-11 02:59:32

+0

嗨,似乎我可以使用accept + _nested_attribute_for:在Contacts模型中用作accep_nested_attributes_for:addresses,因为它是belongs_to关联。我看了一下API,正在尝试has_one ......但是这听起来是对的吗? – Angela 2010-05-11 03:30:20

+0

当您想与其他模型(如公司和个人)共享模型(如Address)时,多态关联非常有用。一个公司可以有一个地址,一个人可以有一个地址,所以如果地址格式相同,重用该模型是有意义的。没有多态性,你需要单独的CompanyAddress和PersonAddress模型。 我会编辑答案以回应您的其他评论。 – zetetic 2010-05-11 07:03:22

0

我建议让联系人有很多地址。它无论如何都发生在野外。如果决定了只有一个,那将是has_one :address

哦,你必须downcase他们:has_many :Contacts必须has_many :contacts

在你引用地址模型(数据库需要addressable_idaddressable_type场)

belongs_to :addressable, :polymorphic => true 
+0

地址如何添加到新的联系人然后可供该公司新的未来联系人使用? – Angela 2010-05-09 17:44:25

1

由于您希望地址既属于公司又属于联系人,因此您有两种选择。如果您不需要多个地址(我可能不推荐这么做),那么您可以在公司和联系人中输入“belongs_to:address”。然后,地址将具有“has_many:companies”和“has_many:contacts”。

更有意义的更好的选择是使用多态关联,其中地址表将具有: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 

我不知道如果你不像这样分别嵌套记录,你怎么能保持所有的链接直。

如果您确实需要获取公司的所有地址以用于显示目的,则可以使用像collect这样的标准ruby枚举操作将它们聚集在一起。

祝你好运!

+0

嗨,我仍然试图决定是否使用多态...从概念上讲,公司的地址是:通过联系人......换句话说,我有一个与公司相关的地址的唯一原因是因为联系人属于它。你对那个怎么想的? – Angela 2010-05-13 01:07:25

+0

是啊,我确实需要看到所有的company.addresses ...它将允许用户选择一个潜在的地址,当添加一个新的联系....所以这意味着不使用多态? – Angela 2010-05-13 01:11:14

+0

多态性是有意义的,因为如果随着您的应用程序的增长,您可能希望将地址与其他对象关联起来。如果你没有预见到这一点,这是没有必要的。 如果不使用多态性,可以使用has_many:through,但仅用于显示目的。您仍然需要使用嵌套表单来访问特定联系人,以便为公司的联系人添加/修改地址。 P.S.这有助于投票吗? – aceofspades 2010-05-13 18:40:35

相关问题