2012-03-28 71 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 
相關問題