rails:如何通过文本字段添加子关联?

时间:2010-01-23 17:07:59

标签: ruby-on-rails activerecord associations

我有两个模型:公司和人

class Person < ActiveRecord::Base
 belongs_to :company
end

class Company < ActiveRecord::Base
  has_many :people
  accepts_nested_attributes_for :people, :allow_destroy => true, :reject_if => proc {|attrs| attrs.all? {|k,v| v.blank? } }
end

我的HTML表单部分用于新建和编辑操作,如下所示:

<% form_for(@company) do |company_f| %>
  <p>
    <b>Name</b><br />
    <%= company_f.text_field :name %>
  </p>

  <ul>
    <%= render :partial => 'person_fields', :collection => @company.people, :locals => {:company_f => company_f} %>
    <%= link_to_add_fields(:people, company_f) %>
  </ul>

  <p>
    <%= company_f.submit "Submit" %>
  </p>
<% end %>

其中“_person_fields”部分看起来像这样:

<li>
  <% company_f.fields_for :people, person_fields do |person_f| %>
    <%= person_f.label :name %>
    <%= person_f.text_field :name %>
  <% end %>
</li>

目前,如果我输入person_f.text_fiel:输入此人的姓名,然后点击“保存”,则会创建一个具有该名称的新Person模型。不是我想要的,我已经在数据库中拥有那个人的Person模型,我宁愿将这个人与这家公司联系起来。

另一件事是,我不介意使用该名称进行人性化识别,而不是像“_person_fields”部分这样的id

<li>
  <% company_f.fields_for :people, person_fields do |person_f| %>
    <%= person_f.label :id %>
    <%= person_f.text_field :id %>
  <% end %>
</li>
顺便说一句,这也不起作用。当我点击提交时,没有任何反应。什么都得不到保存或改变或任何东西

所以我想,仅仅是为了实验,我说我确实使用id来识别Person模型,(因此我不需要使用隐藏的id字段进行自动完成,我将其用于另一个项目。我讨厌它)。我想要的只是:转到一个新的/编辑公司页面,有一堆文本字段供我输入人员ID,然后保存,然后这些人与公司联系。我的意思是,它就像

people = Person.find(1,2,3)
#=>["romeo","juliet","henry"]
company = Company.first
#=>["Shakespeare Co."]
company.people<<people
company.people
#=>["romeo","juliet","henry"]

如果我不必使用选择菜单,那将是最好的,因为最终如果项目起飞并且我有一千人,那对于任何选择菜单来说都太大了。我知道然后我将不得不使用autocomplete + hidden id字段,当选择一个人的名字时,它会被设置。

谢谢!

1 个答案:

答案 0 :(得分:4)

accepts_nested_attributes_for :people定义了公司模型中的people_attributespeople_attributes=方法。当您在表单中fields_for :people时,将使用这两种方法。如documentation中所述(阅读整页,不仅仅是方法定义):

  

嵌套属性允许您通过父级保存关联记录的属性。

关联两个对象不起作用,没有它就可以完成,你在公司中仍然有people方法。

Formtastic例如使用具有多个属性的select(您可以使用ctrl检查多个选项)或check_boxes列表(对普通用户更容易,不必触摸键盘)。

如果你想使用自动完成功能,可以这样做,但是你需要将你的id附加到people[]数组(现在不记得格式了,稍后我会检查。你可以检查它的格式您在表单中为:people创建属性我认为

编辑:
我认为我知道这很简单(没有自动完成,只有简单的html)。

companies/edit(或new)观看位置:

<p>
  <%= company_f.label :people %>
  <%= company_f.collection_select :person_ids, Person.all, :id, :name, {}, {:multiple => true} %>
</p>

这将允许您选择多个人到公司(使用Ctrl)。此部分生成的参数现在应该转移到您的控制器:

"company"=>{"name"=>"CompanyA", "person_ids"=>["1", "3"]}, "commit"=>"Update", "id"=>"4"

如果你想向人们展示复选框,我设法得到它(我从formtastic复制了部分标记):

<ul>
  <% Person.all.each do |person| %>
    <li>
      <%= check_box :person, :id, {:name => "company[person_ids][]", :checked => @company.people.include?(person) }, person.id, nil %>
      <%= person.name %>
    </li>
  <% end %>
</ul>

我今天没时间检查如何使用隐藏字段进行操作,因此您可以使用自动填充,但我相信它应该与复选框类似 - 您需要使用为每个自动填充添加<input type="hidden" name="company[person_ids][]" value="#{person.id}">的内容此人,还需要创建已在公司中的人员的初始列表(您覆盖列表,而不是添加到列表中)。


如果您希望字段添加或删除(单独的字段,抱歉)来自公司的人员,那么这应该有效:

公司模式中的

def add_people=(people_ids)
  ids = people_ids.split(/,/).map(&:to_i)
  person_ids += ids if ids
end
def add_people
  ""
end
def remove_people=(people_ids)
  ids = people_ids.split(/,/).map(&:to_i)
  person_ids -= ids if ids
end
def remove_people
  ""
end

并在您的新/编辑视图中:

<p>
  <%= f.label :add_people %><br />
  <%= f.text_field :add_people %>
</p>
<p>
  <%= f.label :remove_people %><br />
  <%= f.text_field :remove_people %>
</p> 

现在你需要做的就是找到自动完成的JavaScript,将它连接到所有人的ID和名称(可能PeopleController#index, :format => :json应该是好的)并告诉它填充那些text_fields(可以是隐藏字段)如果您使用自动完成功能。)

这种方法应该有效,因为您在公司模型中定义虚拟属性,并通过以"1, 2, 6"格式为其分配字符串,您可以从关联人员的集合中添加/删除这些ID