具有额外属性的has_many through关联的Rails表单?

8

我如何为具有其他属性的 has_many :through 关联生成表单字段?

has_many :through 关系有一个名为 weight 的额外列。

这是联接表的迁移文件:

create_table :users_widgets do |t|
  t.integer :user_id
  t.integer :widget_id
  t.integer :weight

  t.timestamps
end

模型长这样:
User
  has_many :widgets, :through => :users_widgets,
           :class_name => 'Widget',
           :source => :widget
  has_many :users_widgets
  accepts_nested_attributes_for :widgets # not sure if this is necessary


Widget
  has_many :users, :through => :users_widgets,
           :class_name => 'User',
           :source => :user
  has_many :users_widgets
  accepts_nested_attributes_for :users # not sure if this is necessary


UsersWidget
  belongs_to :user
  belongs_to :widget

为了简化,Widget和User只有一个自己的字段叫做“name”,因此User.first.nameWidget.first.name
问题:
1.如何在用户创建/编辑表单中添加下拉选择小部件与相应的重量?
2.如何动态地向用户添加其他Widget表单或向Widget添加其他用户表单以轻松添加或删除这些关系? nested_form_for宝石似乎是完美的解决方案,但我还没有能够使其正常工作。
3.除了模型和表单局部之外,是否需要对我的控制器进行任何更改?
快速提示..我不感兴趣在用户表单中创建新的Widget或在Widget表单中创建新的用户,我只想选择现有对象。
我正在运行Rails 3.1和simple_form 2.0.0dev来生成我的表单。
3个回答

6

请提供更详细的问题描述。我制作了一款简单的应用程序。

源代码在这里:https://github.com/yakjuly/nest_form_example

我将其部署到heroku上,您可以打开页面:http://glowing-lightning-1954.heroku.com/users/new

回答:

  1. 如果您需要让用户表单可以选择带权重的小部件,则需要做更多工作。下拉选择无法满足您的要求。

  2. 我将“nested_form”混合在“bootstrap-rails”插件中,您可以更轻松地添加嵌套字段。

  3. 在我的示例中,您需要添加一个调用选择的动作,并使WidgetsController#create能够响应:js。

代码基于simple_form,您可以试试。


6
我将使用我创建的gem——cocoon,来解决您的问题,它用于处理动态嵌套表单。此外,我还有一个示例项目,展示了不同类型关系的示例。
虽然您的模型没有直接包含在内,但是从中推导出来并不难。在您的模型中,您应该编写以下内容:
class User 
  has_many :users_widgets
  has_many :widgets, :through -> :user_widgets

  accepts_nested_attributes_for :user_widgets, :reject_if => :all_blank, :allow_destroy => true

  #...
end

接下来,您需要创建一个部分视图,其中将列出链接的UserWidgets。将此部分放置在名为users/_user_widget_fields.html.haml的文件中:

.nested-fields
  = f.association :widget, :collection => Widget.all, :prompt => 'Choose an existing widget'
  = f.input :weight, :hint => 'The weight will determine the order of the widgets'
  = link_to_remove_association "remove tag", f

在您的users/edit.html.haml文件中,您可以这样写:
= simple_form_for @user do |f|
  = f.input :name

  = f.simple_fields_for :user_widgets do |user_widget|
    = render 'user_widget_fields', :f => user_widget
  .links
    = link_to_add_association 'add widget', f, :user_widgets

希望这能帮到您。

谢谢@nathanvda,这正是我在寻找的。你的宝石使这特别容易。PS:赏金在我能够给予之前已经自动分配了,但我已经联系管理员寻求帮助。 - Marco
几乎设置了完全相同的模型,这非常有帮助。我发现像这样复杂的表单非常令人费解,感谢您提供如此好的宝石/答案! - Ryan Haywood
我知道这不是在SO上好的评论形式,但我只想说非常感谢你。我一直在尝试弄清楚如何将cocoon应用于连接表格,已经尝试了大约6个小时,几乎要放弃了。 - neanderslob

0
非常感谢nathanvda提供的cocoon指针。在尝试在rails 4.0.0-rc1下实现此功能时,我一直在苦苦思索一些问题,并且我想分享我的发现,以防其他人在尝试使用rails4时遇到相同的问题。
以上面的代码为例,我将user_id和widget_id添加到了允许的参数中,因为它们保存在连接表user_widgets中。在rails 3中,您必须将它们添加到用户模型的attr_accesible中,但在rails 4中,您必须将它们添加到用于嵌套的主模型控制器中的允许参数中,因此在这里,那将是users_controller:
params.require(:user).permit(...user_fields...,  
  user_widgets_attributes: [:user_id, :widget_id])

只做这个,你最终会遇到几个问题:

  1. 更新用户记录时,每个关联(小部件)都会被复制。当更新并保存记录时,1变成2、4、8等。
  2. 删除关联不起作用,字段从表单中删除,但关联仍然存在于数据库中。

为了解决这些问题,您还需要将:id和:_destroy添加到允许属性列表中:

params.require(:user).permit(...user_fields...,  
  user_widgets_attributes: [:user_id, :widget_id, :id, :_destroy])

之后它就完美地运行了。

Juergen

附注:目前,您必须在Gemfile中使用git存储库才能在Rails 4下使用cocoon,直到发布与Rails 4兼容的gem。感谢nathanvda在我的错误报告中发送的电子邮件!


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