【问题标题】:Rails: Manipulating a Join Table in a Nested FormRails:以嵌套形式操作连接表
【发布时间】:2013-01-19 21:21:00
【问题描述】:

我有两个模型通过连接表连接:

class Publication < ActiveRecord::Base
  attr_accessible :title, :author_attributes, :translator_attributes

  has_many :publication_contributors
  has_many :authors, :through => :publication_contributors, :source => :contributor,
    :conditions => {:publication_contributors => {:contributor_type => "Author"}}
  has_many :translators, :through => :publication_contributors, :source => :contributor,
    :conditions => {:publication_contributors => {:contributor_type => "Translator"}}

  accepts_nested_attributes_for :authors, :translators
end

class Contributor < ActiveRecord::Base
  attr_accessible :name

  has_many :publications, :through => :publication_contributors
  has_many :publication_contributors
end

class PublicationContributor < ActiveRecord::Base
  attr_accessible :contributor_type

  belongs_to :publication
  belongs_to :contributor
end

请注意,连接表有第三个属性(除了publication_id 和contributor_id),称为contributor_type。此属性可能包含诸如“作者”、“翻译者”、“编辑”或其他角色之类的角色。在我的发布模型中,我为两个更常见的贡献者类型“作者”和“翻译者”创建了一对关联。这些关联可以很好地检索相关数据,例如@publication.authors。但是,当通过嵌套表单创建这些关联时,它会出现问题。

我的表单看起来像这样:

<%= form_for @publication, :remote => true do |f| %>

  <%= f.label :title %>:
  <%= f.text_field :title %>

  <%= f.fields_for :authors do |builder| %>
    <%= builder.label :name, "Author" %>:
    <%= builder.text_field :name %>
  <% end %>

  <%= f.fields_for :translators do |builder| %>
    <%= builder.label :name, "Translator" %>:
    <%= builder.text_field :name %>
  <% end %>

  <%= f.submit %>
<% end %>

在我的控制器中:

def create
  publication = Publication.create(params[:publication])
end

表单按预期呈现,但在创建操作过程中出现异常。我曾希望 Rails 知道根据 Publication 关联中的条件神奇地分配正确的contributor_type,例如:

has_many :authors, :through => :publication_contributors, :source => :contributor,
:conditions => {:publication_contributors => {:contributor_type => "Author"}}

不幸的是,它没有。我在创建过程中收到此错误:

Mysql2::Error: Column 'contributor_type' cannot be null

这让我觉得我唯一的办法是在before_create 回调中手动分配contributor_type,但如果是这种情况,我如何确定要分配哪种类型?参数名称中有正确的类型,例如:

publication[authors_attributes][0][name]

有什么方法可以访问模型层中的信息吗?

更新:

我的new 操作:

def new
  @publication = Publication.new
  publication_contributor = @publication.publication_contributors.build
  contributor = publication_contributor.contributor.build
end

它在最后一行抛出这个错误(一个设置contributor):

undefined method `build' for nil:NilClass

【问题讨论】:

  • 您可以尝试在表单上添加隐藏字段吗?
  • 我不确定这是否会有所帮助。 contributor_type 属性在连接表中,而不是在 Contributor 模型中。由于我实际上并没有在表单中声明连接表,因此我无权访问其属性,即使使用隐藏字段也是如此。但是,如果有一种方法可以在表单中创建连接表,那么这种方法可能会奏效。我会调查一下。如果您有任何其他建议,请告诉我。
  • 你是对的。如果你通过publication_contributor然后contributor可以吗?
  • 我会试一试,看看效果如何。
  • 不确定是否可行。为了让它工作,我必须像这样从 PublicationContributor 创建一个 Contributor 实例:publication_contributor.contributor.new。这会引发 undefined method 'new' for nil:NilClass 错误。我得到与.build 相同的结果。我想知道 ActiveRecord 是否不支持从子级到父级的嵌套。 docs 不会以任何方式提及这种情况。

标签: ruby-on-rails nested-forms jointable


【解决方案1】:

您可以尝试以下方法吗?

class Publication < ActiveRecord::Base
  attr_accessible :title, :publication_contributors_attributes

  has_many :publication_contributors
  has_many :authors, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Author' } }
  has_many :translators, through: :publication_contributors, source: :contributor, conditions: { publication_contributors: { contributor_type: 'Translator' } }

  accepts_nested_attributes_for :publication_contributors
end

class Contributor < ActiveRecord::Base
  attr_accessible :name

  has_many :publications, through: :publication_contributors
  has_many :publication_contributors
end

class PublicationContributor < ActiveRecord::Base
  attr_accessible :contributor_type

  belongs_to :publication
  belongs_to :contributor

  accepts_nested_attributes_for :contributor
end

<%= form_for @publication, :remote => true do |f| %>

  <%= f.label :title %>:
  <%= f.text_field :title %>

  <%= f.fields_for :publication_contributors do |pc_form| %>
    <%= pc_form.hidden_field :contributor_type %>
    <%= pc_form.fields_for :contributor do |c_form| %>
      <%= c_form.label :name, "Author" %>:
      <%= c_form.text_field :name %>
    <% end %>
  <% end %>

  <%= f.submit %>
<% end %>

更新:控制器代码

@publication.publication_contributors.build
@publication.publication_contributors.each do |pc|
  pc.build_contributor
end

【讨论】:

  • 拍了这张照片,但我仍然收到构建错误。奇怪的是,类似的情况也适用于 RailsCast
  • 我已经更新了我的答案,你的控制器至少应该是什么样子:)
  • 当然!即使我只创建了其中一个,publication_contributors 仍然是一个协会。奇怪的是,我不能用publication_contributors = @publication.publication_contributors.build 设置它,然后用publication_contributors.each 直接访问它。不过,您的代码运行良好。那谢谢啦!我无法直接访问publication_contributors,这让我很困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多