简体   繁体   English

Rails - CanCan HABTM关联检查

[英]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)

Edit: 编辑:

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 manage Contact c where c.reseller_id is the user_id of some Contact in user.contacts user可以管理Contact c ,其中c.reseller_iduser.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM