3

场景:我有一个 has_many 关联(Post 有很多作者),并且我有一个嵌套的 Post 表单来接受作者的属性。

我发现当我调用 post.update_attributes(params[:post]) 其中 params[:post] 是一个带有 post 和所有要添加的作者属性的哈希时,似乎没有办法让 Rails 只如果满足某些条件,则创建作者,例如作者的用户名已经存在。如果用户名在模型中具有唯一性验证,Rails 会做的只是失败并回滚 update_attributes 例程。如果没有,Rails 将添加一个新记录 Author,如果没有 id 的记录在哈希中。

现在我在 Post 控制器中的更新操作代码变成了这样:

def update
  @post = Post.find(params[:id])

  # custom code to work around by inspecting the author attributes
  # and pre-inserting the association of existing authors into the testrun's author
  # collection
  params[:post][:authors_attributes].values.each do |author_attribute|
    if author_attribute[:id].nil? and author_attribute[:username].present?
      existing_author = Author.find_by_username(author_attribute[:username])
      if existing_author.present?
        author_attribute[:id] = existing_author.id 
        @testrun.authors << existing_author
      end
    end
  end

  if @post.update_attributes(params[:post])
    flash[:success] = 'great!'
  else
    flash[:error] = 'Urgg!'
  end

  redirect_to ...
end

有没有更好的方法来处理我错过的这个问题?

编辑:感谢@Robd'Apice 让我考虑覆盖默认的 authors_attributes= 函数,该函数代表我插入到模型中,我能够想出更好的东西:

def authors_attributes=(authors_attributes)
  authors_attributes.values.each do |author_attributes|
    if author_attributes[:id].nil? and author_attributes[:username].present?
      author = Radar.find_by_username(radar_attributes[:username])
      if author.present?
        author_attributes[:id] = author.id
        self.authors << author
      end
    end
  end
  assign_nested_attributes_for_collection_association(:authors, authors_attributes, mass_assignment_options)
end

但是我对此并不完全满意,一方面,我仍然直接从调用者那里获取属性哈希,这需要了解这些哈希的逻辑是如何工作的(例如:id set or not set),以及两个,我正在调用一个适合此处的函数。如果有办法告诉“accepts_nested_attributes_for”只在不满足特定条件时创建新记录,那就太好了。一对一关联有一个 :update_only 标志,它执行类似的操作,但缺少一对多关系。

有更好的解决方案吗?

4

2 回答 2

1

这种逻辑可能属于您的模型,而不是您的控制器。我会考虑重写author_attributes=默认为您的关联创建的方法。

def authors_attributes=(authors_attributes)
  authors_attributes.values.each do |author_attributes|
    author_to_update = Author.find_by_id(author_attributes[:id]) || Author.find_by_username(author_attributes[:username]) || self.authors.build
    author_to_update.update_attributes(author_attributes)
  end
end

我还没有测试过该代码,但我认为应该可以。

编辑:要保留的其他功能accepts_nested_Attributes_for,您可以使用super

def authors_attributes=(authors_attributes)
  authors_attributes.each do |key, author_attributes|
    authors_attributes[key][:id] = Author.find_by_username(author_attributes[:username]).id if author_attributes[:username] && !author_attributes[:username].present?
  end
  super(authors_attributes)
end 

如果该实现super不起作用,您可能有两个选择:继续对控制器中的属性哈希进行“处理”(但将其转换为控制器的私有方法以稍微清理一下),或者继续我的第一个解决方案是添加您从自己的代码中丢失的功能:destroy => truereject_if这不会太难)。我可能会选择第一个选项。

于 2012-06-29T07:25:58.560 回答
0

我建议使用表单对象,而不是尝试让 accept_nested_attributes 工作。我发现表单对象通常更干净、更灵活。看看这个 railscast

于 2014-01-10T19:32:36.360 回答