Ruby on rails Rails有许多带有额外属性的复选框

Ruby on rails Rails有许多带有额外属性的复选框,ruby-on-rails,ruby,has-many,Ruby On Rails,Ruby,Has Many,下面的代码用于带有复选框的复杂rails表单。我对我们现有的解决方案不是很满意,我想知道是否有人知道在rails中实现这一点的更合适的方法。下面所有的代码都在工作,我只是想知道是否有更干净的方法 在我的Admins controller中,我想消除每次更新时调用以下代码的需要 @user.admin.school_admin_roles.destroy_all params[:roles].each do |school_role| ids = school_role.split('_'

下面的代码用于带有复选框的复杂rails表单。我对我们现有的解决方案不是很满意,我想知道是否有人知道在rails中实现这一点的更合适的方法。下面所有的代码都在工作,我只是想知道是否有更干净的方法

在我的Admins controller中,我想消除每次更新时调用以下代码的需要

@user.admin.school_admin_roles.destroy_all
params[:roles].each do |school_role|
    ids = school_role.split('_')
    @user.admin.school_admin_roles.find_or_create_by_school_id_and_school_role_id(ids[0], ids[1])
end if !params[:roles].nil?
因此,我基本上希望能够调用@user.update_属性(params[:user]),并让rails为我创建所需的关系。我在下面的表格中有一个与AccountRole一起工作的角色。我想知道,如果联接表中有一个额外的变量school_id,是否有办法对SchoolRole执行相同的操作

我们有以下表单用于编辑用户和分配角色

表单截图->

我有以下表格,管理员可以编辑其他用户,并通过复选框分配基于帐户的角色和基于学校的角色。基于帐户的角色易于实现。基于学校的规则有点复杂,因为联接表school_admin_roles有school_id、user_id、role_id字段。我们必须以一种相当粗俗的方式实现表单中的学校角色部分。我们的表单是这样实现的-请注意我们是如何将school.id.to_s+“_'+role.id.to_s合并到学校角色的同一复选框中的

在管理员控制器的更新功能中,我们在每次更新中手动销毁所有学校管理员角色,然后通过学校角色参数循环,在“-”上对ID进行拆分,然后手动重新创建每个基于学校的角色。我真的很讨厌我们这样做。有谁能为解决这个问题提供一种更干净、更以rails为中心的方法

形式-

<%= form_for @user, :url => {:controller => 'admins', :action => 'update'} do |f| %>
    <%= f.label :username %>
    <%= f.text_field :username %>

    <%= f.fields_for :admin do |uf| %>
    <div class="field">
        <%= uf.label :first_name %>
        <%= uf.text_field :first_name %>
    </div>

<label>Admin Permissions</label>  
    #account level permissions works fine
    <%= hidden_field_tag "#{uf.object_name}[account_role_ids][]"   %>
    <% AccountRole.find(:all).each do |role| %>
    <div class="account_role">
        <%= check_box_tag "#{uf.object_name}[account_role_ids][]", role.id,  @user.admin.account_roles.include?(role)%>
    <%= role.name %>
    </div>
    <% end %>

   #school level permissions a bit of a hack    
    <%= hidden_field_tag "#{uf.object_name}[school_role_ids][]"   %>
        <% SchoolRole.find(:all).each_with_index do |role, index| %>
        <div class="school_role">
        <%= check_box_tag "#{uf.object_name}[school_role_ids][]",role.id, @user.admin.school_roles.include?(role) %>
        <%= role.name %>
        <span class="advanced_box admin_permissions" <% if @user.admin.school_roles.include?(role) %>style="display:inline"<% end %>>
        <div class="content" id="perm_<%= index %>">
        <h4><%= role.name %></h4>
    <% uf.object.account.schools.each do |school|%>
    <div>
    <%= check_box_tag "roles[]", school.id.to_s+'_'+role.id.to_s, role.school_admin_roles.where(:admin_id => uf.object.id).collect(&:school_id).include?(school.id)%>
    <%= school.name %>
</div>
<% end %>
<%= link_to 'Done', '#', :class => "done" %>
</div>
<a href="#" class="open"> Advanced</a>
</span>
</div>
<% end %>
</div>
<% end %>
{:controller=>'admins',:action=>'update'}do | f |%>
管理权限
#帐户级别权限可以正常工作
#学校级别的权限有点像黑客
收集(&:学校id)。包括?(学校id)%>
“完成”%>
控制器

class AdminsController < ApplicationController
def update
    @user = User.find(params[:id])
    if  @user.update_attributes(params[:user])

      # TODO find a way to refactor this 
      @user.admin.school_admin_roles.destroy_all
      params[:roles].each do |school_role|
        ids = school_role.split('_')
        @user.admin.school_admin_roles.find_or_create_by_school_id_and_school_role_id(ids[0], ids[1])
      end if !params[:roles].nil?
      #

      flash[:notice] = "Successfully updated Admin."
      redirect_to admins_path
    else
      render "edit"
    end
  end
end
class AdminsController
鉴于以下模型

class User < ActiveRecord::Base
  has_one :parent
  has_one :admin
  has_many :scool_admin_roles
  has_many :account_admin_roles
end

class AccountAdminRole < ActiveRecord::Base
  before_save :set_account_id

  belongs_to :admin
  belongs_to :account_role
end

class SchoolAdminRole < ActiveRecord::Base
  belongs_to :admin
  belongs_to :school_role
  belongs_to :school
end

class SchoolRole < ActiveRecord::Base
  has_many :school_admin_roles
end


class AccountRole < ActiveRecord::Base
  has_many :account_admin_role
end
class用户
当我面对我知道很难闻的代码时,它通常会引导我去设计

在这种情况下,问题在于数据库表的设计

您正在对从带有分隔符的复选框传递的值进行黑客攻击,因为“join”表不仅仅是join。我相信与学校的关系属于学校的角色,而不是学校的管理角色。更改此选项将创建一个与AccountRole非常相似的模式


纠正模型设计,现在可能有点痛苦,但它更干净,将来可以维护。稍后您将感谢您自己。

我们对上述代码进行了如下重构

<% index2 = 0 %>
<% SchoolRole.find(:all).each_with_index do |role, index| %>
<div class="school_role">
<%= check_box_tag "school_roles[]",role.id, @user.admin.school_roles.include?(role) %>
<%= role.name %>
<span class="advanced_box admin_permissions" <% if @user.admin.school_roles.include?(role) %>style="display:inline"<% end %>>
div class="content" id="perm_<%= index %>">
<h4><%= role.name %></h4>
                                <%  uf.object.account.schools.each do |school|%>
<div>
<%=    check_box_tag "#{uf.object_name}[school_admin_roles_attributes][#{index2}][school_role_id]", role.id, role.school_admin_roles.where(:admin_id => uf.object.id).collect(&:school_id).include?(school.id)%>
                                            <%= school.name %>
                                        <%= hidden_field_tag "#{uf.object_name}[school_admin_roles_attributes][#{index2}][school_id]", school.id %>
                                    </div>
                                    <% index2 += 1 %>
                            <% end %>
                            <%= link_to 'Done', '#', :class => "done" %>
                    </div>
                    <a href="#" class="open"> Advanced</a>
                </span>
            </div>
            <% end %>
        </div>
    <% end %>
在这个模型中,我们为:school_admin_roles添加了accepts_nested_attributes_,:reject_if=>proc{attr | attr['school_role_id']。blank?}

并在attr\u accessible中添加了school\u admin\u roles\u属性

class Admin < ActiveRecord::Base
  belongs_to :account
  belongs_to :user

  has_many :school_admin_roles
  has_many :school_roles, :through => :school_admin_roles
  has_many :account_admin_roles
  has_many :account_roles, :through => :account_admin_roles

  accepts_nested_attributes_for :account

  accepts_nested_attributes_for :school_admin_roles, :reject_if => proc { |attr|  attr['school_role_id'].blank? }

  attr_accessible :account_role_ids, :email, :first_name, :last_name, :account_id, :user_id, :account_attributes, :school_admin_roles_attributes
  default_scope where(:deleted => false)
end

你能解释一下你为什么不喜欢你的东西吗?它不起作用吗?你不喜欢表格的书写方式吗?你认为这会打开一个安全漏洞吗?我觉得你要求的改进在3+段文字中消失了。这实际上是一个比我们经常看到的好得多的问题,但是你能把你的问题/目标重新表述为你文章的第一句话,并用支持的段落来阐述吗?我想说谢谢你的截图,包括所有相关的代码-这两个都是非常有用的。我还建议张贴在codereview上,因为它更适合。。。好。。。检查代码。我认为这个数据库设计没有问题。我们的SchoolAdminRole表用于指定角色、用户和他拥有该角色的学校。我们的规范要求我们的角色系统是这样的粒度。@Marcello很公平,你肯定比我更了解你的规范和你面临的问题。在我看来,为了适应这种结构,你将面临大量的工作。如果学校直接与学校角色有关系,而不是与学校管理员角色有关系,那么维护起来会更容易。
def update
    @user = User.find(params[:id])
    @user.admin.school_admin_roles.destroy_all
    if  @user.update_attributes(params[:user])
      flash[:notice] = "Successfully updated Admin."
      redirect_to admins_path
    else
      render "edit"
    end
  end