【问题标题】:rails habtm: return associated records but with exclusive matchrails habtm:返回关联记录但具有排他匹配
【发布时间】:2012-04-11 23:57:50
【问题描述】:

维护具有自定义基于角色的授权系统的现有 Rails 2.3.x 应用程序。

代码是这样的:

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

如果不求助于编写原始 SQL 查询,我无法理解如何在 Rails 中执行此操作。

有什么建议吗?


更新: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 替换对 scoped() 的调用(update: DOES NOT)解决了计数问题。

因此,为了正确/简洁,我将 Chris 的提议与一些编辑结合在一起。谢谢!


另一个更新。
其实 :group 和 : 没有通过的问题也在 named_scoped 实现中。

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

太好了……

【问题讨论】:

    标签: ruby-on-rails join has-and-belongs-to-many


    【解决方案1】:

    我认为您不需要直接编写 SQL 查询,但我认为您需要一个带有一些 SQL 组和子句的 named_scope 来执行您要查找的操作:

    在 app/models/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  }
    } 
    

    在 app/models/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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      • 2011-04-29
      相关资源
      最近更新 更多