Ruby on rails Rails有许多带有额外属性的复选框
下面的代码用于带有复选框的复杂rails表单。我对我们现有的解决方案不是很满意,我想知道是否有人知道在rails中实现这一点的更合适的方法。下面所有的代码都在工作,我只是想知道是否有更干净的方法 在我的Admins controller中,我想消除每次更新时调用以下代码的需要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('_'
@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