[英]Rails - CanCan HABTM association checks
I have models set up as follows (self-association in contacts because information I wanted to store for resellers mirrored all fields in that table, seemed in keeping with DRY to use the already existing data structures): 我的模型设置如下(联系人中的自我关联,因为我想为经销商存储的信息镜像该表中的所有字段,似乎与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
With cancan, I want to have a reseller login that is able to manage their own contact. 使用cancan,我想拥有一个能够管理自己联系的经销商登录。 The mapping between users and resellers is HABTM, so this can be achieved by doing
can :manage Contact, :users => {:id => user.id}
as below. 用户和经销商之间的映射是HABTM,因此可以通过以下方式实现
can :manage Contact, :users => {:id => user.id}
,如下所示。
I also want the reseller login to be able to manage all Contact's which match the set described by managed_accounts in the following logic: 我还希望经销商登录能够在以下逻辑中管理与managed_accounts描述的集合匹配的所有Contact:
reseller_contacts = user.contacts
managed_accounts = []
reseller_contacts.each do |i|
managed_accounts << i.reseller_clients
end
managed_accounts.flatten!
My current Ability class has: 我目前的能力课程有:
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
The error I receive with it as it is, is as follows: 我收到它的错误如下:
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))
My understanding of cancan is that it checks on a per contact basis what is and isn't permitted. 我对cancan的理解是它根据每个联系人检查什么是允许的和不允许的。 If I could do what I wanted in a block, it would appear as follows (Covers both the resellers own contact and all contacts which are clients of the reseller):
如果我可以在一个块中执行我想要的操作,它将显示如下(涵盖经销商自己的联系人和所有经销商客户的联系人):
can :manage, Contact do |contact|
user.contacts.exists?(contact.reseller_id) || user.contacts.exists?(contact.id)
end
I can't use a block for this however, as when trying to use @contacts = Contact.accessible_by(current_ability)
in my index action on the controller, I get: 我不能使用块,因为当我在控制器上的索引操作中尝试使用
@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)
ALMOST solved, now I just have a problem of combining abilities: 几乎已经解决了,现在我只是遇到了一个组合能力的问题:
I changed the working part of my Ability model to read as: 我将Ability模型的工作部分改为:
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
Now the only problem is that the first can :manage
line gets overwritten by the second one. 现在唯一的问题是第一个问题
can :manage
线被第二个覆盖。 I was under the impression that they should be additive, not replacing. 我的印象是它们应该是添加剂,而不是替代品。 More research required, but I think this question itself is fixed by the above.
需要更多的研究,但我认为这个问题本身是由上述问题解决的。 Now I need to work out how to make both
can :manage
lines apply. 现在我需要弄清楚如何使两者都
can :manage
行应用。
Edited 2015-03-26 编辑2015-03-26
Having noticed that this question/answer was getting a bit of attention I thought I should point out a better method I've found since. 注意到这个问题/答案得到了一些关注,我想我应该指出一个我以后发现的更好的方法。
When creating has_one/has_many associations, rails creates foreign_model_id
/ foreign_model_ids
methods respectively. 创建has_one / has_many关联时,rails分别创建
foreign_model_id
/ foreign_model_ids
方法。 These methods return an integer or array of integers respectively. 这些方法分别返回整数或整数数组。
That means instead of the answer below, the entry in the ability.rb
file can be simplified without having to use that ugly logic to create my own array of objects and iterate through them to: 这意味着,而不是下面的答案,才能简化
ability.rb
文件中的条目,而不必使用那个丑陋的逻辑来创建我自己的对象数组并迭代它们:
can :manage, Contact, id: (user.contact_ids + user.reseller_client_ids)
Previous answer kept for posterity 以前的答案留给后代
Fixed by using this in my Ability.rb file: 通过在我的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, thanks for your help along the way, don't think I'd have got there without your comments. Deefour,谢谢你一路走来的帮助,如果没有你的意见,我不认为我会去那里。
I think you're still not wording your request as clearly as you could 我认为你仍然没有尽可能清楚地写出你的要求
...the id of the reseller's own Contact
...经销商自己的联系人的ID
The :reseller
of a Contact
is another Contact
. :reseller
Contact
:reseller
是另一个Contact
。 There is no :contact
attribute in Contact
. 没有
:contact
的属性Contact
。 Perhaps you're making things confusing by referring to "reseller role" and "reseller" when you should be referring to user (from the CanCan
class) to avoid confusion with the Contact
class' :reseller
association) . 当您应该引用用户 (来自
CanCan
类)以避免与Contact
类混淆时,您可能会通过引用“经销商角色”和“经销商”来使事情变得混乱:reseller
协会 。
I will assume 我会假设
reseller role to be able to manage all of the Contacts which have the reseller_id field set to the id of the reseller's own Contact.
经销商角色能够管理将reseller_id字段设置为经销商自己的联系人ID的所有联系人。
to mean 意思是
user
can manageContact
c
wherec.reseller_id
is theuser_id
of some Contact inuser.contacts
user
可以管理Contact
c
,其中c.reseller_id
是user.contacts
中某些联系人的user_id
Assuming this is an accurate interpretation: 假设这是一个准确的解释:
can :manage, Contact do |c|
user.contacts.where(:user_id => c.reseller_id)
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.