4

我的模型设置如下(联系人中的自关联,因为我想为经销商存储的信息反映了该表中的所有字段,似乎符合 DRY 以使用现有的数据结构):

class Contact < ActiveRecord::Base
  attr_accessible :reseller_id
  has_and_belongs_to_many :users
  has_many :reseller_clients, :class_name => "Contact", :foreign_key => "reseller_id"
  belongs_to :reseller, :class_name => "Contact"
end

class User < ActiveRecord::Base
  attr:accessible :name
  has_and_belongs_to_many :contacts
end

使用 cancan,我希望有一个能够管理自己联系人的经销商登录。用户和经销商之间的映射是HABTM,因此可以通过can :manage Contact, :users => {:id => user.id}以下方式实现。

我还希望经销商登录能够管理与 managed_accounts 在以下逻辑中描述的集合匹配的所有联系人:

reseller_contacts = user.contacts
managed_accounts = []
reseller_contacts.each do |i|
  managed_accounts << i.reseller_clients
end
managed_accounts.flatten!

我目前的能力课程有:

class Ability
  include CanCan::Ability
  def initialize(user)
    if user.role? :reseller
      # Allow resellers to manage their own Contact
      can :manage, Contact, :users => {:id => user.id} # This works correctly at present
      # Allow resellers to manage their client Contacts
      can :manage, Contact, :reseller => {:users => {:id => user.id}} #This doesn't work
    end
  end
end

我收到的错误如下:

Mysql2::Error: Unknown column 'contacts.users' in 'where clause': SELECT `contacts`.* FROM `contacts` INNER JOIN `contacts` `resellers_contacts` ON `resellers_contacts`.`id` = `contacts`.`reseller_id` INNER JOIN `contacts_users` ON `contacts_users`.`contact_id` = `resellers_contacts`.`id` INNER JOIN `users` ON `users`.`id` = `contacts_users`.`user_id` INNER JOIN `contacts_users` `users_contacts_join` ON `users_contacts_join`.`contact_id` = `contacts`.`id` INNER JOIN `users` `users_contacts` ON `users_contacts`.`id` = `users_contacts_join`.`user_id` WHERE ((`contacts`.`users` = '---\n:id: 6\n') OR (`users`.`id` = 6))

我对 cancan 的理解是,它会根据每个联系人检查允许和不允许的内容。如果我可以在一个块中做我想做的事情,它将如下所示(包括经销商自己的联系人和作为经销商客户的所有联系人):

can :manage, Contact do |contact|
  user.contacts.exists?(contact.reseller_id) || user.contacts.exists?(contact.id)
end

但是,我不能为此使用块,因为当尝试@contacts = Contact.accessible_by(current_ability)在控制器上的索引操作中使用时,我得到:

The accessible_by call cannot be used with a block 'can' definition. The SQL cannot be determined for :index Contact(id: integer, first_name: string, last_name: string, postal_addr_line_1: string, postal_addr_line_2: string, postal_addr_line_3: string, postal_addr_city: string, postal_addr_post_code: string, postal_addr_country: string, billing_addr_line_1: string, billing_addr_line_2: string, billing_addr_line_3: string, billing_addr_city: string, billing_addr_post_code: string, billing_addr_country: string, contact_email: string, company_name: string, phone_home: string, phone_work: string, phone_mobile: string, split_bills: boolean, created_at: datetime, updated_at: datetime, reseller_id: integer)

编辑:

几乎解决了,现在我只是有一个组合能力的问题:

我将能力模型的工作部分更改为:

reseller_contacts = user.contacts
managed_accounts = []
reseller_contacts.each do |i|
  i.reseller_clients.each do |rc|
    managed_accounts << rc.id
  end
end

can :manage, Contact, :id => managed_accounts
can :manage, Contact, :users => {:id => user.id}
can :create, Contact

现在唯一的问题是第一can :manage行被第二行覆盖。我的印象是它们应该是附加的,而不是替代的。需要更多的研究,但我认为这个问题本身已由上述解决。现在我需要弄清楚如何使两条can :manage线都适用。

4

2 回答 2

8

2015-03-26 编辑

注意到这个问题/答案引起了一些关注,我想我应该指出我从那以后找到的更好的方法。

创建 has_one/has_many 关联时,rails 分别创建foreign_model_id/foreign_model_ids方法。这些方法分别返回一个整数或整数数组。

这意味着ability.rb可以简化文件中的条目而不是下面的答案,而不必使用那种丑陋的逻辑来创建我自己的对象数组并遍历它们以:

can :manage, Contact, id: (user.contact_ids + user.reseller_client_ids)

以前的答案留给后代

通过在我的 Ability.rb 文件中使用它来修复:

# Manage all contacts associated to this reseller
reseller_contacts = user.contacts
managed_contacts = []
reseller_contacts.each do |i|
  i.reseller_clients.each do |rc|
    managed_contacts << rc.id
  end
  managed_contacts << i.id
end


can :manage, Contact, :id => managed_contacts

Deefour,感谢您一路上的帮助,不要以为没有您的评论我就不会到达那里。

于 2012-07-12T21:40:00.230 回答
1

我认为你仍然没有尽可能清楚地表达你的要求

...经销商自己的联系人的 id

:resellera 的是Contact另一个Contact。中没有:contact属性Contact。当您应该指代用户(来自CanCan类)以避免与Contact类的:reseller关联混淆时,您可能会通过引用“经销商角色”和“经销商”来使事情变得混乱)

我会假设

经销商角色,以便能够管理所有将 reseller_id 字段设置为经销商自己的联系人 ID 的联系人。

意思是

user可以管理一些联系人在Contact c哪里c.reseller_iduser_iduser.contacts

假设这是一个准确的解释:

can :manage, Contact do |c|
  user.contacts.where(:user_id => c.reseller_id)
end
于 2012-07-12T12:57:58.387 回答