ruby-on-rails - 如何正确编写具有 N-N 关系的嵌套表单

标签 ruby-on-rails syntax nested-forms

在开发 Rails 3 应用程序时,我遇到了嵌套表单的问题。

一个集合是一组预定义的对象(使用 db:seed 创建)。 另一个集合应该显示一个表单以允许选择一些元素。

示例比长描述更好,所以就在这里。

假设您有 2 个模型:用户和组。

假设有几个组:成员、管理员、访客……

您希望您的用户拥有多个组,因此您需要一个中间表:成员资格。

模型代码很明显:

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships

  accepts_nested_attributes_for :memberships, :allow_destroy => true
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :users, :through => :memberships
end

Controller 不需要更改。 然而,该 View 更为复杂。

我想显示一个复选框列表来选择几组预定义的复选框。

我在这里使用特殊的_destroy字段,具有相反的值,在实际未选中时销毁(因此在选中时将用户添加到组中)

%p
  = f.label :name
  %br
  = f.text_field :name
%ul  
  = f.fields_for :memberships, @groups do |g|
    %li
      - group = g.object
      = g.hidden_field :group_id, :value => group.id
      = g.check_box :_destroy, {:checked => @user.groups.include?(group)}, 0, 1
      = g.label :_destroy, group.name

但是,这并不能按预期工作,因为形式 g将始终在每个组之后创建一个具有任意 id 的输入(甚至通过在 </li> 之后包含它来破坏布局):

<input id="user_memberships_attributes_0_id" name="user[memberships_attributes][0][id]" type="hidden" value="1" />
<input id="user_memberships_attributes_1_id" name="user[memberships_attributes][1][id]" type="hidden" value="2" />
# ...

了解嵌套属性的语法如下:

{:group_id => group.id, :_destroy => 0} # Create
{:group_id => group.id, :_destroy => 0, :id => membership.id} # Update
{:group_id => group.id, :_destroy => 1, :id => membership.id} # Destroy
{:group_id => group.id, :_destroy => 1} # Do nothing

每次id都发送是行不通的,因为它会尝试更新一条不存在的记录而不是创建它,当记录不存在时尝试销毁。

我找到的当前解决方案是删除所有 id,无论如何它们都是错误的(它们应该是成员资格的 id,而不是简单的索引),并在用户已经拥有该组时添加真实的 id。 (这在创建和更新之前在 Controller 中调用)

def clean_memberships_attributes
  if membership_params = params[:user][:memberships_attributes]
    memberships = Membership.find_all_by_user_id params[:id]
    membership_params.each_value { |membership_param|
      membership_param.delete :id
      if m = memberships.find { |m| m[:group_id].to_s == membership_param[:group_id] }
        membership_param[:id] = m.id
      end
    }
  end
end

这看起来太错误了,它在 Controller 中添加了很多逻辑,只是为了控制 View 的不良行为fields_for helper 。

另一个解决方案是自己创建所有表单 html,尝试模仿 Rails 约定,并避免 id 问题,但这在代码中确实很吵,我相信有更好的方法。

  • 这是一种制作 fields_for 的方法吗?工作得更好吗?
  • 有没有更合适的 helper ?
  • 我在这个问题中的推理是否有错误?
  • 您将如何实现这一目标?

谢谢

最佳答案

我希望我理解正确吗?

组是预定义的,您希望能够将用户添加到组中。 在编辑用户屏幕上,您将显示全部或部分预定义组。 您想要通过勾选复选框并保存记录来添加用户。 如果取消选中该复选框,则该用户在未选中组中的成员资格应该为零。

以下是我对公司和项目的做法:

Class Company
  has_many :datasets
  has_many :projects, :through => :datasets

Class Project
  has_many :datasets
  has_many :companies, :through => :datasets


<% for company in Company.all %>
  <tr>
    <td>
      <%= check_box_tag 'project[company_ids][]', company.id, @project.companies.include?(company) %>
    </td>
      </tr>
<% end %>

我列出了所有公司并选中我想要包含在项目中的公司。

请告诉我这是否对您有帮助?我相信你在你的例子中使用 haml 。我不太习惯这种表示法。

如果您想使用子集,您可以使用范围:

scope :recent, Company.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight)

然后你可以像 .all 方法一样使用这个作用域:

Company.recent

这有帮助吗?

关于ruby-on-rails - 如何正确编写具有 N-N 关系的嵌套表单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3622312/

相关文章:

haskell - Haskell 中的重口音

ruby-on-rails - 嵌套表单上的 update_attributes 不起作用

javascript - 动态添加嵌套表单

ruby-on-rails - Ruby on Rails : Operation now in progress - connect(2) would block

ruby-on-rails - Ubuntu 上的 Ruby Gem Guard,没有 'guard' 命令

ruby-on-rails - 为什么我的 bootstrap-sass 规则被用户代理规则覆盖?

javascript - 评论在 JS 中是如何分类的?

Javascript for 循环而不重新计算数组的长度

ruby-on-rails - Rails 多种帖子类型 : nested forms and complex association

ruby-on-rails - Ruby `require` 在 rails lib 中找不到我的文件