7

为简洁起见编辑我的问题并更新我所做的事情:

如何为公司建模具有多个地址并将单个地址分配给联系人,并能够在创建或编辑联系人时分配它们?

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

我似乎无法让它工作。我得到了一个未定义的方法“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

这是我的地址模型:

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

以下是我尝试在 View for _form for Contact 中创建字段的方式,以便在有人创建新联系人时,他们将地址传递给地址模型并将该地址与联系人相关联:

  <% 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#create (for 127.0.0.1 at 2010-05-12 21:16:17)

[POST] 参数:{"commit"=>"Submit", "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 "companies" WHERE ("companies"."name" = 'Allyforce') LIMIT 1[0m

地址创建 (16.0ms)[0m
[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

联系人创建 (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

4

4 回答 4

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

如果您查看源代码,您的输入字段应该是这样的。

< input type="text" size="30" name="contact[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 %>

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

 <% 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。我建议使用添加 address_attributes 和删除(切换样式)的 javascript 代码创建一个链接。

于 2010-05-12T07:02:49.100 回答
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

当然,您可以将地址列移动到联系人表中,并完全摆脱地址模型。

如果每个地址有多个联系人(即,地址是联系人工作的公司设施):

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是一个很好的介绍。

编辑——更多关于嵌套属性

accepts_nested_attributes_for应该与 ahas_manyhas_oneassociation 一起使用,并且应该与多层嵌套一起使用。似乎你想让一个联系人属于一个公司,一个地址属于一个联系人,在这种情况下:

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 的优秀示例,涵盖了几种典型情况。

于 2010-05-09T06:50:59.597 回答
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

如果您不像这样单独嵌套记录,我不确定如何保持所有链接直接。

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

祝你好运!

于 2010-05-12T16:36:21.830 回答
0

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

哦,你必须小写它们:has_many :Contacts必须是has_many :contacts

在您引用的地址模型中(数据库需要一个addressable_idaddressable_type字段)

belongs_to :addressable, :polymorphic => true
于 2010-05-09T07:05:37.040 回答