Rails嵌套表单和has_many :through,如何编辑关联模型的属性?

105

当使用accepts_nested_attributes_for时,如何编辑关联模型的属性?

我有三个模型:主题和文章由链接器连接。

class Topic < ActiveRecord::Base
  has_many :linkers
  has_many :articles, :through => :linkers, :foreign_key => :article_id
  accepts_nested_attributes_for :articles
end
class Article < ActiveRecord::Base
  has_many :linkers
  has_many :topics, :through => :linkers, :foreign_key => :topic_id
end
class Linker < ActiveRecord::Base
  #this is the join model, has extra attributes like "relevance"
  belongs_to :topic
  belongs_to :article
end

所以当我在主题控制器的“新”操作中构建文章时...

@topic.articles.build

...并使topics/new.html.erb中的嵌套表单...

<% form_for(@topic) do |topic_form| %>
  ...fields...
  <% topic_form.fields_for :articles do |article_form| %>
    ...fields...

...Rails自动创建了链接器,这很棒。 现在我的问题是:我的链接器模型还有一些属性,我希望能通过“新主题”表单来更改这些属性。但是,Rails自动创建的链接器除了topic_id和article_id之外,所有属性都具有nil值。我该如何将那些其他链接器属性的字段放入“新主题”表单中,以便它们不为空?


3
我正在尝试做和你一样的事情,只不过是在新建/创建操作中...我想知道你能否分享控制器的操作。我想通过一个 Relationship 作为一个 linker,通过一个 Account 来创建一个 User...但是我无法弄清楚这些新建和创建操作应该是什么样子的...你介意分享一下吗? - Mohamad
https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through - zx1986
3个回答

93

找到了答案。关键在于:

@topic.linkers.build.build_article

这将构建链接器,然后为每个链接器构建文章。因此,在模型中:


topic.rb 需要 accepts_nested_attributes_for :linkers


linker.rb 需要 accepts_nested_attributes_for :article

然后在表单中:

<%= form_for(@topic) do |topic_form| %>
  ...fields...
  <%= topic_form.fields_for :linkers do |linker_form| %>
    ...linker fields...
    <%= linker_form.fields_for :article do |article_form| %>
      ...article fields...

13
Rails 3 更新:如果你在使用 Rails 3,那么 form_for 和 field_for 需要使用 <%= %> 而不是 <% %>。请注意不改变原意,使语言更加通俗易懂。 - Arcolye
我会在你添加的两个accepts_nested_attributes_for行周围添加代码反引号。当我只是浏览代码时,我一直错过了这些信息 - 一旦我仔细阅读它,我就发现了这个遗漏的细节,并解决了我的问题。谢谢! - T.J. Schuck
2
老实说,这是 rubyonrails.org 指南需要的完整示例。 - ahnbizcad
视觉清晰确实能起到很大的作用,T.J. Schuck。 - ahnbizcad
@Arcolye,你的强参数是什么?我遇到了类似的问题。你能帮帮我吗? - user1301037

7
当由Rails生成的表单提交到Rails的controller#action时,params的结构类似于以下内容(添加了一些虚构属性):
params = {
  "topic" => {
    "name"                => "Ruby on Rails' Nested Attributes",
    "linkers_attributes"  => {
      "0" => {
        "is_active"           => false,
        "article_attributes"  => {
          "title"       => "Deeply Nested Attributes",
          "description" => "How Ruby on Rails implements nested attributes."
        }
      }
    }
  }
}

注意,linkers_attributes实际上是一个从零开始索引的带有String键的Hash,而不是Array。这是因为发送到服务器的表单字段键看起来像这样:

topic[name]
topic[linkers_attributes][0][is_active]
topic[linkers_attributes][0][article_attributes][title]

现在创建记录就像这样简单:

TopicController < ApplicationController
  def create
    @topic = Topic.create!(params[:topic])
  end
end

我不确定,但我认为accepts_nested_attributes_for一直被假定了。 - Arcolye
3
在当时,像这样的协会在互联网上找到这些信息真是太麻烦了 - 也许那天我的谷歌功夫不行。我想至少在这里记录下来,因为我和我的同事都以为Rails将linked_attributes转换为数组,而不是一个从零开始索引的哈希表。希望这个小贴士能对未来的某个人有所帮助 :) - Daniel Doezema

3

在使用has_one时需要注意的一个小细节。 我将复制用户KandadaBogguthis thread中给出的答案。


build 方法的签名在 has_onehas_many 关联中是不同的。

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

has_many关联的构建语法:

user.messages.build

has_one关联的构建语法:

user.build_profile  # this will work

user.profile.build  # this will throw error

阅读文档了解更多关于has_one关联的细节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接