5

这很奇怪。我在我的 rails 3.2 应用程序中使用 Rolify + CanCan + Devise。我的用例很简单。我希望用户一次只有一个角色,因此要更改角色,我会执行以下操作:

user.remove_role "admin"
user.add_role "associate"

对我来说奇怪的是,当我这样做时,角色“admin”会从 Roles 表中删除。为什么会这样?我不想完全消除这个角色,只是从用户那里获得一个给定的角色。我究竟做错了什么?

这是SQL。注意最后一个 delete from 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
4

4 回答 4

4

基本问题是角色名称、资源类型和资源标识的每个组合在roles表中只存储一次。如果您删除此行,则会为所有人删除。

然后解决方案是从join tablerolify 中仅删除连接UserRole模型的行。为了便于访问,我将连接表作为模型来使用一些 rails 魔法来生成 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

它只会删除你想要的。

请注意,这不会删除具有角色的表行,我将保留它以备将来使用。

于 2014-01-24T12:42:16.710 回答
2

我知道这个问题已有两年多的历史了,但我找到了更好、更“喜欢”的答案。
您可以在用户模型中非常容易地做到这一点:

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 - here method remove_role cals method "remove" in adapter file: rolyfi: role_adapter.rb

于 2015-11-27T11:22:51.930 回答
1

这就是我解决此问题的方法:

模型/用户.rb

def delete_roles
  roles.delete(roles.where(:id => self.roles.ids))
end

用户控制器.rb

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

每次创建或更新用户时,删除用户的角色。

于 2015-12-16T20:50:58.140 回答
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 将删除角色。

于 2021-02-19T17:46:21.263 回答