Rails 3:nested_form,collection_select,accepts_nested_attributes_for和fields_for

时间:2011-06-07 19:11:22

标签: ruby-on-rails ruby-on-rails-3 fields-for nested-form-for

Update: answered here

这里有许多好的问题和答案,关于获取nested_form,collection_select,accepts_nested_attributes_for和fields_for可以很好地一起玩的互联网,但我仍然难过。如果你能帮助我,请提前致谢。

目标:生成新的isbn记录。 isbn可以有很多贡献者。我成功地使用ryanb nested_form gem根据需要在表单上动态生成新的贡献者字段。其中一个字段使用Contributor中的所有名称记录的collection_select下拉列表。创建新记录时,需要将许多贡献者ID写入连接表(contributors_isbns)。

我有一些工作,但只是我可以将一个贡献者ID保存到isbns表中的新记录。我似乎无法将任何数据写入连接表。

我有三个型号。贡献者和Isbns有很多关系,我用has_many做过:通过。 isbn可以拥有许多贡献者,贡献者可以拥有许多isbns。他们通过contributors_isbn加入。

isbn.rb

  attr_accessible               :contributor_id
  has_many                      :contributors, :through => :contributors_isbns
  has_many                      :contributors_isbns
  accepts_nested_attributes_for :contributors
  accepts_nested_attributes_for :contributors_isbns

contributor.rb

  attr_accessible               :isbn_id
  has_many                      :contributors_isbns
  has_many                      :isbns, :through => :contributors_isbns
  accepts_nested_attributes_for :isbns

contributors_isbn.rb

  class ContributorsIsbn
  attr_accessible               :isbn_id, :contributor_id
  belongs_to                    :isbn
  belongs_to                    :contributor
  accepts_nested_attributes_for :contributors

在isbns控制器中:

 def new
    @isbn  = Isbn.new
    @title = "Create new ISBN"
    1.times {@isbn.contributors.build}
    @isbn.contributors_isbns.build.build_contributor
  end

(显然我无法理解使用哪种构建方法。)

在isbns new.html.erb视图中:

<%= nested_form_for @isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
  <div class="actions">
    <%= f.submit "Create" %>
  </div>  

<% end %>

在_fields partial中,一个带有非常纯文本字段的版本:

<%= field_set_tag 'Contributor' do %>
<%= f.link_to_add "Add Additional Contributor", :contributors %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>

<%= f.fields_for :contributors_isbns, :validate => false do |contrib| %>
<li>
<%= contrib.label :id, 'contributors_isbns id' %>
<%= contrib.text_field :id %>
</li>
<% end %>

<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole  %>
</li>

<% end %>

在这里,一个不起作用的发烧友版本:

<%= f.fields_for :contributors_isbns, :validate => false do |contributors| %>
<li>
<%= f.label :personnameinverted, 'Contributor Name' %>
<%= f.collection_select(:contributor_id,  Contributor.all, :id, :personnameinverted ) %>
</li>
<% end %>

此代码使用here的答案。两者都导致nested_form_for @isbn行上出现“丢失阻止”错误。

提前再次感谢。

更新:here是关于nested_form gem的一些信息,它可能会在查看此类问题时派上用场。这里有一个关于accepts_nested_attributes_for的[2009但仍然相关的帖子] [4]。

更新2:嗯,这是件事。我一直在两个不同的模型中探讨这个的缩减版本,不是使用collection_select或has_many,而只是简单的belongs_to / has_many关联。父模型是Contract,子模型是Istc。我甚至无法通过嵌套表单创建记录。但是,在查看堆栈并搜索错误消息“警告。无法批量分配受保护的属性”后,我刚刚将:istcs_attributes添加到我的:attr_accessible行,现在我可以添加记录。一个相当重要的位丢失,以及RTFM的情况,因为它就在gem自述文件中。我稍后会更新,看看这是否适用于更复杂的has_many通过关联。

更新4:[这里] [5]是关于如何处理nil记录错误消息的另一个有用的帖子。

更新5:轻微绕行 - 当我向表单动态添加一组新字段时,正在创建一个子记录。 Duh - 我在子表单区域中有“添加”链接。这是以前的:

<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<%= f.link_to_add "[+] Add an istc", :istcs  %>
<% end %>

这是后面的事情:

<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<% end %>
<%= f.link_to_add "[+] Add an istc", :istcs  %>

更新6,回复后:

哦,不。 collection_select不起作用。它正在添加新的贡献者记录,而不是使用贡献者模型中的现有记录。 Someone else也遇到了这个问题。有任何想法吗?

1 个答案:

答案 0 :(得分:3)

好哇!这是使所有这些工作的代码。有点啰嗦,但不想遗漏任何东西。我的主要经验是:

  • 您需要在父模型中使子属性attr_accessible

  • 您需要在联接表模型中创建父级和子级ID attr_accessible

  • 如果在父控制器中至少构建一个子实例,它会让生活更轻松。

contributor.rb模型

class Contributor < ActiveRecord::Base
  attr_accessible  #nothing relevant 
  has_many :contributors_isbns
  has_many :isbns, :through => :contributors_isbns

isbn.rb model

class Isbn < ActiveRecord::Base
  attr_accessible :contributors_attributes, :contributor_id, :istc_id #etc
  belongs_to  :istc
  has_many   :contributors, :through => :contributors_isbns
  has_many   :contributors_isbns
  accepts_nested_attributes_for :contributors #if you omit this you get a missing block error

contributors_isbn model

class ContributorsIsbn < ActiveRecord::Base
  belongs_to :isbn
  belongs_to :contributor
  attr_accessible :isbn_id, :contributor_id

isbn controller

 def new
    @isbn  = Isbn.new
    @title = "Create new ISBN"
    1.times {@isbn.contributors.build}
  end

new.html.erb

<td class="main">
<%= nested_form_for @isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
  <div class="actions">
    <%= f.submit "Create" %>
  </div>  

<% end %>

_fields.html.erb

<%= field_set_tag 'Identifier Details' do %>

<li>
<%= f.label 'Title prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titleprefix %>
</li>
<li>
<%= f.label 'Title without prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titlewithoutprefix %>
</li>
<li>
<%= f.label 'ISTC' %>
<%= f.collection_select(:istc_id, Istc.all, :id, :title_title_text, :prompt => true) %>
</li>

<% end %>


<%= field_set_tag 'Contributor' do %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>

<%= f.fields_for :contributors, :validate => false do |contributor_form| %>
<li>
<%= contributor_form.label :personnameinverted, 'Contributor Name' %>
<%= contributor_form.collection_select(:isbn_id, Contributor.all, :id, :personnameinverted ) %>
</li>
<%= contributor_form.link_to_remove "[-] Remove this contributor"%>
<% end %>
<%= f.link_to_add "[+] Add a contributor", :contributors  %>


<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole  %>
</li>

<% end %>