2012-03-28 35 views
0

维护具有基于角色的自定义授权系统的现有Rails 2.3.x应用程序。rails habtm:返回关联的记录但独占匹配

代码必须是这样的:

class Role << AR:Base 
    # has an int attribute called "level" with higher values indicating more powerful role 
    habtm: members 
end 

class Member << AR:Base 
    habtm: roles 
end 

角色表有类似
(id, name, level)
1, admin, 1000
2, VIP, 500
3, regular, 100
4, some_other_role, 50

我有以下成员说明角色
的member1(角色:adminVIPregular
member2(角色:VIPregular
member3(角色:regular

我需要有时是拉起人员根据其分配的最高角色:

Role.admins_exclusively # should return member1 
Role.vips_exclusively  # should return just member2 
Role.regulars_exclusively # should be just member3 

无法将我的头围绕如何在Rails中执行此操作,而无需诉诸写入原始SQL查询。

有什么建议吗?


更新:2012年3月29日
这是我的解决方案基本上确定了一堆为每个角色这样的(()使用以及一些动态规划与define_method一起)的方法。

class Member < AR:Base 
    define_method :vips_exclusively do 
    scoped :joins => :roles, 
    :group => 'members.id', 
    :having => ["max(roles.level) = ?", Role.find_by_name('vip').level] 
    end 
end 

但是,我发现旧的rails 2.3.x有一个问题。例如,在Member.vips_exclusively上调用size()或count()会产生不正确的总数。调用length()会产生正确的结果,但建议尽可能使用size()。

查看Rails代码后,看起来像:group:having之类的选项在scoped()中设置时不会传递给count()。用named_scopes替换调用范围()(更新:不是)解决了计数问题。

因此,我将克里斯的建议与一些编辑结合起来,以保证正确性和简洁性。谢谢!


另一个更新。
其实问题:group和:没有被传递也在named_scoped实现中。

果然,这里有一张陈旧的票据,没有任何修复,使它成为Rails源代码树(至少不在2.3.x分支中)。
https://rails.lighthouseapp.com/projects/8994/tickets/1349-named-scope-with-group-by-bug

太棒了......

回答

0

我不认为你需要直接写SQL查询,但我认为你需要一些SQL组named_scope和having子句做你找什么:

在应用程序/模型/ member.rb

named_scope :maximum_level, lambda { |level| { 
    :having => [ 'MAX(roles.level) = ?', level ], 
    :group => 'members.id', # edited to need quotes 
    :joins => :roles # dont need the whole join statement } 
} 

在应用程序/模型/ role.rb

def exclusive_members 
    Member.maximum_level(self.level).all 
end 

def self.members_by_role_name(role_name) 
    role = self.find(:conditions => ['name = ?', role_name]).first 
    role.exclusive_members 
end 

示例

>> r = Role.find(1) 
=> <Role id:1, name:"admin", level:1000> 
>> r.exclusive_members 
=> [ list of members with a highest role of "admin"] 
>> r.exclusive_members.map { |m| m.name } 
=> [ "member1" ] 
>> Role.members_by_role_name("admin") 
=> # the same list as you'd get by calling r.exclusive_members 
相关问题