CanCan 分离角色模型

3
我一直在关注this的指南,其中介绍了CanCan中的分离角色模型实现。 当一个User尝试注册时,在创建Assignment时会出现此错误。

User(#21477600) 期望得到,但 Symbol(#5785720) 被获取

我正在使用由Devise生成的User,具有以下before_save函数。

class User < ActiveRecord::Base
.
.
.
 def create_profile
    profile = Profile.new :user_id => :id
  end

  def create_role
     Assignment.new :user => :id, :role => Role.find_by_role("user").id
  end
end

我希望将用户的角色默认设置为“用户”,但显然我做错了什么。应该如何实现?

4个回答

9

不确定您是否已经看过这个,但是Ryan Bates制作了一份关于:

独立角色模型的精彩文档。

编辑:

以下是我目前在使用的代码。我相信您的“Assignment”与我的“UserRole”是相同的。

user.rb

#--
# Relationship
has_many :user_roles, :dependent => :destroy, :uniq => true
has_many :roles, :through => :user_roles, :uniq => true

#--
# Instance Method

# Determine if the user has a specified role
# You can find this method at: https://github.com/ryanb/cancan/wiki/Separate-Role-Model
# Written by Ryan Bates, I added the downcase though to detect 'Admin' vs 'admin'.
# Example:
#       user.has_role? :Admin
#       => true
def has_role?(role_sym)
  roles.any? { |role| role.name.underscore.to_sym == role_sym.downcase }
end

role.rb

#  id         :integer(4)      not null, primary key
#  name       :string(255)  

#--
# Relationship
has_many :user_roles, :dependent => :destroy, :uniq => true
has_many :users, :through => :user_roles, :uniq => true

user_role.rb

#  id         :integer(4)      not null, primary key
#  user_id    :integer(4)
#  role_id    :integer(4)

#--
# Relationship
belongs_to :user
belongs_to :role

然后在我的 ability.rb 文件中

def initialize(user)
  user ||= User.new                   # in case of a guest
  if user.has_role? :Admin            # The user is an Administrator
    can :manage, :all
  else
    can :read, :all
  end
end

接下来我可以轻松地分配角色,就像在我的种子文件中一样:

# Create Users
...

# Roles
admin = Role.create!(:name => "admin")
standard = Role.create!(:name => "standard")

# UserRoles :Admin
user1.roles << admin
user2.roles << standard

所以通过调用 user.roles << [role_name],我实际上创建了一个 UserRole,它具有 user_id 和 role_id。


这是我在问题中提到的文档。我正在使用他概述的Assignment、Role和User模型。我只是在为角色创建任务时遇到了一些麻烦。 - nathan
1
你把admin = Role.create!(:name => "admin") standard = Role.create!(:name => "standard")放在哪里? - Vasseurth
1
Planetpluto,你需要将这段代码放入你的seeds.rb文件中。然后你就可以调用rake db:seed来填充你的数据库了。 - ardavis

3

也许有更有效的方法来完成这个任务,但是如果没有确切的模型关联,我无法确定。

无论如何,我认为以下方法应该有效:

def create_role
  Assignment.new :user => self, :role => Role.find_by_role("user")
end

由于您指定了 :user 而不是 :user_id,因此您应该传递 self。对于 :role 也是如此。如果您指定了 :role_id,则在 find_by_role 后输入 .id,但由于您仅指定了 :role,则删除 .id


2

看起来你正在将符号传递给期望对象的哈希条件中。

DanneManne的回答应该可以解决问题。或者你也可以采用另一种方法:

Assignment.new( :user_id=>self.id, :role_id => Role.find_by_role('user').id )

(但我认为Danne的更好)

最后一个建议--为什么不把角色的名称称为“name”,而不是“role”呢?这样,您将执行Role.find_by_name('user')。这对于后续的程序员来说会更容易跟随。


2

首先,你不应该使用“保存回调”(save callback),因为它会在创建和更新时都触发。

其次,如果你设置了模型之间的关联,就像这样:

class User < ActiveRecord::Base
  has_one :profile
  has_many :assignments
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

class Assignment < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
end

您将拥有像user.profileuser.build_profileuser.create_profile等方便的方法。Build&create会自动设置user_id到配置文件中。您可以在回调函数中使用它们,无需定义任何方法。
请注意,在用户保存之前它并没有id。因此,您需要使用before_create :build_profile或者after_create :create_profile中的一个。第一个将在内存中创建概要,并在用户被保存后自动保存,第二个则非常直接。
对于任务分配也会有类似的方法:user.assignments.builduser.assignments.create。因此,User的最终代码大致如下:
class User < ActiveRecord::Base
  has_one :profile
  has_many :assignments
  after_create :create_profile, :create_assignment

  def create_assignment
    assignments.create :role => Role.find_by_role("user")
  end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接