使用has_many:through关联定义能力(CanCan)

3
我是一名Rails初学者,一直在查看许多针对这个问题的建议答案。但似乎我无法将我看到的答案翻译成适合我的情况。希望有人能够友好地给我一些指引。
我关心的两个模型是用户(User)和项目(Project)。每个用户可以创建一个项目,但每个用户也可以管理一个项目。为了实现这一点,我已经创建了一个has_many :through关系,将UserProject连接起来,使用一个管理模型来连接这两个模型。

user.rb

 has_many :managings, foreign_key: "manager_id", dependent: :destroy
 has_many :managed_projects, through: :managings

managing.rb

belongs_to :manager, class_name: "User"
belongs_to :managed_project, class_name: "Project"

project.rb

has_one :reverse_managing, foreign_key: "managed_project_id",
                                class_name:  "Managing",
                                dependent:   :destroy
has_one :manager, through: :reverse_managing, source: :manager

所以,我有一个称为“初学者”的角色。初学者只能阅读项目,除了那些选择成为项目经理的人。经理还应该被允许编辑和更新他们正在管理的项目。

下面是我在ability.rb中尝试的内容。

 user ||= User.new # guest user (not logged in)
  if user.role? :Admin
    can :manage, :all
  elsif user.role? :Author
    can :read, :all
    can [:create], [Project]
    can [:edit, :update], Project, :user_id => user.id
  elsif user.role? :Beginner
   can :read, :all

   can [:edit, :update], Project, :manager_id => user.id
  end

最后一行似乎没有起作用,因为当我查看一个初学者管理的项目时,我没有看到编辑链接。

这是我在视图中使用的代码:

<% if can? :update, project  %>
<%= link_to 'Edit', edit_project_path(project) %>
<% end %>

这是出现在projects_controller中的内容。

def edit
  @project = Project.find(params[:id])
  authorize! :edit, @project
end

如果有人能帮助我解决这个问题,我将会非常高兴。

更新

以下是User.rb文件中的role?方法:

def role?(role)
  return !!self.roles.find_by_name(role.to_s.camelize)
end

更新

我错误地给用户分配了错误的角色 ID。在更正后,当我尝试打开包含编辑链接的项目视图时,出现了错误提示。

undefined method `manager_id' for #<Project:0xb387f0b0>

你确定 role? 方法正在执行它应该执行的操作吗?:Beginner 看起来有点奇怪。 - Logan Serman
让我理解一下,您在elsif块内定义了一个:Beginner可以编辑和更新项目的规则,前提是提供了manager_id => user.id。似乎有些不对劲。 - Deej
初学者有成为项目经理的选项。在这种情况下,他们被允许管理特定的项目。当我说管理时,我的意思是他们可以在现实世界中管理项目的运作。这与管理资源无关。因此,逻辑应该是“允许初学者编辑或更新项目,只要他们已经将自己分配为项目经理”。初学者自愿管理该项目。 - madasahatta
1个回答

8
问题在这里:
can [:edit, :update], Project, :manager_id => user.id

cancan的能力条件只能使用数据库列(根据cancan wiki)。在这里,cancan期望Project模型中有一个名为manager_id的列,但实际上并不是这样。你的模型看起来有点像:

User [id, ...]
Project [id, ...]
Managings [manager_id, managed_project_id, ...]

manager_id是Managings中的一个字段,而不是Project中的。

解决方法是使用另一个wiki页面中描述的块条件。我没有测试过,但我认为以下内容将起作用 -

can [:edit, :update], Project do |p|
 p.manager == user
end

1
@abhishek,但是如果项目有很多经理怎么办?p.managers.map(&:id).include? user.id - gaussblurinc

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