这很奇怪。我在我的rails 3.2 app中使用了Rolify + CanCan + Devise。我的用例很简单。我希望用户一次只能拥有一个角色,从而改变角色,我会这样做:
user.remove_role "admin"
user.add_role "associate"
对我来说奇怪的是,当我这样做时,角色“admin”会从Roles表中删除。为什么会这样?我不想完全消除角色,只是来自用户的给定角色。我做错了什么?
这是SQL。注意最后一次从roles语句中删除:
3] pry(main)> u.remove_role "sub_admin"
Role Load (0.1ms) SELECT "roles".* FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = 2 AND "roles"."name" = 'sub_admin'
(0.0ms) begin transaction
(0.3ms) DELETE FROM "users_roles" WHERE "users_roles"."user_id" = 2 AND "users_roles"."role_id" IN (2)
(1.9ms) commit transaction
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "users_roles" ON "users"."id" = "users_roles"."user_id" WHERE "users_roles"."role_id" = 2
(0.0ms) begin transaction
SQL (2.1ms) DELETE FROM "roles" WHERE "roles"."id" = ? [["id", 2]]
(0.6ms) commit transaction
答案 0 :(得分:4)
基本问题是每个组合,如果role-name,resource_type和resource_id只在roles
表中存储一次。如果删除此行,则会删除所有人。
然后,解决方案是仅删除连接join table
和User
模型的rolify Role
中的行。为了便于访问,我将使连接表成为使用某些rails magic生成SQL的模型。由于这实际上是一种服务对象,我将使它成为一个类单例。
这是我的黑客:
class UsersRoles < ActiveRecord::Base
def self.delete_role(subject,role_symbol, obj=nil)
res_name = obj.nil? ? nil : obj.class.name
res_id = obj.id rescue nil
role_row = subject.roles.where(name: role_symbol.to_s, resource_type: res_name , resource_id: res_id).first
if role_row.nil?
raise "cannot delete nonexisting role on subject"
end
role_id = role_row.id
self.delete_all(user_id: subject.id,role_id: role_id)
end
private_class_method :new
end
此代码未经过优化,但应该让您知道该怎么做:例如,您现在可以为User
模型添加便捷方法:
def delete_role(role_symbol,target=nil)
UsersRoles.delete_role self,role_symbol,target
end
然后你可以说:
user.delete_role :admin
它只会删除你想要的东西。
请注意,此不会删除具有该角色的表格行,我将保留该角色以备将来使用。
答案 1 :(得分:1)
我知道这个问题已经超过两年了,但我发现这个问题更好,更像“回复”这样的答案 您可以在用户模型中轻松完成:
def remove_only_role_relation(role_name)
roles.delete(roles.where(:name => role_name))
end
并使用它:
@user = User.find_by_id(params[:id])
role_name = params[:role_name]
# or:
# role_name = Role.find_by_id(params[:role_id]).name rescue nil
if @user and role_name
@user.remove_only_role_relation(role_name)
end
我在他们的消息来源中找到了类似的代码:
rolyfi: role.rb - 此处方法remove_role cals方法适用文件中的“删除”:rolyfi: role_adapter.rb
答案 2 :(得分:0)
这就是我解决这个问题的方法:
def delete_roles
roles.delete(roles.where(:id => self.roles.ids))
end
def add_role
@user.delete_roles
role = params[:user][:roles]
@user.add_role Role.find(role).name if not role.blank?
@user.role_id = Role.find(role).id if not role.blank?
end
每次创建或更新用户时,请删除用户的角色。
答案 3 :(得分:0)
转到config/initializers/rolify.rb
取消注释这一行
config.remove_role_if_empty = false
所以文件现在看起来像这样:
Rolify.configure do |config|
# Configuration to remove roles from database once the last resource is removed. Default is: true
config.remove_role_if_empty = false
end
默认情况下,remove_role_if_empty 设置为 true
,如果角色未分配给任何其他用户,则 rolify 将删除该角色。