Rolify remove_role удаляет из таблицы ролей?

Это странно. Я использую Rolify + CanCan + Devise в своем приложении rails 3.2. Мой вариант использования прост. Я хочу, чтобы у пользователя была только одна роль за раз, поэтому, чтобы изменить роль, я делаю что-то вроде этого:

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

Странно для меня то, что когда я делаю это, роль "администратор" удаляется из таблицы ролей. С чего бы это? Я не хочу полностью исключать эту роль, просто данную роль от пользователя. Что я делаю неправильно?

Вот SQL. Обратите внимание на последнее удаление из роли:

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 ответа

Основная проблема заключается в том, что каждая комбинация, если имя-роли, resource_type и resource_id хранится только один раз в roles Таблица. Если вы удалите эту строку, она будет удалена для всех.

Решение состоит в том, чтобы удалить только строки из join table Ролифы соединяют User и Role моделей. Для простоты доступа я сделаю таблицу соединений моделью, использующей магию рельсов для генерации 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

и это будет только удалить то, что вы хотите.

Обратите внимание, что это не удалит строку таблицы с ролью, которую я бы сохранил для будущего использования.

Я знаю, что этому вопросу уже более двух лет, но я нашел лучший и более "похожий" ответ.
Вы можете сделать это очень просто в пользовательской модели:

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 метод "remove" в файле адаптера: rolyfi: role_adapter.rb

Вот как я решил эту проблему:

модели / user.rb

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

user_controller.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

Каждый раз, когда пользователь создается или обновляется, удаляйте роли пользователя.

Идти к 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 удалит роль, если она не назначена другому пользователю.

Другие вопросы по тегам