通过与其他属性相关联来创建has_many的Rails表单?

时间:2011-12-02 07:20:32

标签: ruby-on-rails ruby nested-forms has-many-through simple-form

如何为具有其他属性的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的字段,ergo User.first.nameWidget.first.name

问题:

  • 如何将具有相应权重的窗口小部件的下拉列表选项附加到用户创建/编辑表单?

  • 如何向用户或用户表单动态添加其他窗口小部件表单到窗口小部件以轻松添加或删除这些关系? nested_form_for宝石似乎是完美的解决方案,但我无法让它发挥作用。

  • 除了模型和表格部分之外,是否需要对我的控制器进行任何更改?


快速注释..我对在用户窗体中创建新窗口小部件或在窗口小部件窗体中创建新用户不感兴趣,我只希望能够从现有对象中进行选择。

我正在运行Rails 3.1和simple_form 2.0.0dev来生成表单。

3 个答案:

答案 0 :(得分:6)

根据你的问题。我做了一个简单的应用程序 来源在这里:https://github.com/yakjuly/nest_form_example 我将其部署到heroku,您可以打开页面:http://glowing-lightning-1954.heroku.com/users/new

答案

  1. 您希望用户表单可以选择具有权重的小部件,需要做更多的工作。下拉选择不能满足您的要求。

  2. 我在“bootstrap-rails”插件中混合了“nested_form”,你可以更容易地添加nested_fields

  3. 在我的例子中,你需要添加一个动作调用select,并使WidgetsController #create can responsed_with:js

  4. 代码基于simple_form,你可以尝试一下。

答案 1 :(得分:6)

我将使用cocoon解决您的问题,我创建了一个用于处理动态嵌套表单的gem。我还有an example project来展示不同类型关系的示例。

你的字面意思并不完全包含在内,但并不难从中衍生出来。在你的模型中你应该写:

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

希望这有帮助。

答案 2 :(得分:0)

非常感谢cocoon指针nathanvda。我一直在试图在rails 4.0.0-rc1下实现这个问题时遇到的一些问题,我想我会分享我的发现,以防有人在尝试这个udner 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. 删除关联不起作用,该字段将从表单中删除,但关联仍保留在数据库中。
  3. 要解决这些问题,您还需要将:id和:_destroy添加到允许的属性列表中:

    params.require(:user).permit(...user_fields...,  
      user_widgets_attributes: [:user_id, :widget_id, :id, :_destroy])
    
    之后,它完美无缺。

    尔根

    PS:现在你必须使用你的Gemfile中的git存储库在rails 4下使用cocoon,直到释放了rails 4兼容的gem。感谢我的错误报告中的nathanvda电子邮件!!