加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-17 01:57:53 所属栏目:百科 来源:网络整理
导读:在处理Rails 3应用程序时,我遇到了嵌套表单的问题. 一个集合是一组预定义对象(使用db:seed创建). 另一个集合应该显示一个表单,以允许选择一些元素. 一个例子比长描述更好,所以在这里. 假设您有2个模型:用户和组. 假设有几个组:成员,管理员,客人,…… 您希
在处理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

控制器不需要更改.
然而,观点更复杂.

我想显示一个复选框列表,以选择一些预定义的组.

我在这里使用特殊的_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)},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.
(在创建和更新之前在控制器中调用)

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

这似乎是错误的,它在控制器中添加了很多逻辑,只是为了控制视图fields_for帮助程序的不良行为.

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

>是否可以让fields_for更好地工作?
>有没有更合适的助手?
>我在这个问题的某个地方推理错了吗?
>你将如何做到这一点?

谢谢

解决方法

我希望我理解正确吗?

这些组是预定义的,您希望能够将用户添加到组中.
在编辑用户屏幕上,您将显示所有或部分预定义组.
您想通过勾选复选框并保存记录来添加用户.
如果取消勾选该复选框,则未绑定组中此用户的成员身份应为零.

以下是我在公司和项目中的表现:

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

这有帮助吗?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读