ActiveRecord跨三个模型查询?

时间:2011-01-21 17:59:15

标签: ruby-on-rails ruby-on-rails-3

我有三种模式:

class User < ActiveRecord::Base
 has_many :projects, :through => :permissions

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project
 belongs_to :role

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

通过以上方式获取项目的所有用户非常容易:@project.users

但我想做的是得到这样的结果:获取所有用户项目中的所有用户。

因此,如果用户有3个项目,每个项目有5个用户。我想查询以获取所有用户组中的所有15个用户。

我正在尝试。

 current_user.projects.users 

但是Rails并不喜欢那么多。 current_user.projects效果很好,但不是用户。

连连呢?想法?谢谢!

更新代码3基于noodl的评论

  scope :suggestedContacts, lambda { |user|
    users_from_projects = user.projects.reduce([]) {|all_users,prj|
      all_users + prj.users
    }.uniq
  }

错误:

NoMethodError(#的未定义方法`includes_values'):

6 个答案:

答案 0 :(得分:3)

我的两个解决方案是:

  • 干净,简单的标准rails元代码,没有自定义查找程序
  • 高效,在一个SQL查询中提取,没有N + 1个问题
  • 合规,这意味着您仍然可以在关系
  • 上构建,创建等

1。

您可以在用户类中链接您的关系 由于rails 3.0.x尚不支持嵌套的has_many_through,因此您可以使用this插件直到rails 3.1

class User < ActiveRecord::Base
 has_many :permissions
 has_many :projects, :through => :permissions
 has_many :users_in_projects, :through => :projects, :source => :user # chain the relation

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

current_user.users_in_projects

2

另一种方式是急切加载和减少(就像其他答案已经描述的那样,但我会更明确地说明。) 这是更多的工作,更少的依赖。

class User < ActiveRecord::Base
 has_many :permissions
 has_many :projects, :through => :permissions, :include => :users # eager load users

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

current_user.projects.map(&:users).reduce(&:+).uniq_by(&:id) 
# returns users in current_user's projects, one query, some computations

答案 1 :(得分:2)

@noodl有正确的想法,但是它确实会导致“一些”开销。除非您谈论拥有数百万个项目和权限,否则您将不会注意到通过优化获得的改进。无论如何,这是一种替代方法,理论上,虽然不是很好,但可以“有效地”为您提供所需的内容(假设您的表格被正确编入索引)。

class User

  has_many :project_users, :class_name => "User", :finder_sql => 'select u.* from users u join permissions pp on pp.user_id=u.id join projects p on pp.project_id=p.id where project_id in (select project_id from permissions pp2 where user_id=#{id}) and pp.user_id != #{id} group by u.id'

end

这不是范围,但允许你说:

user = User.find(1)
=> #<User id: 1, name: "Stephen">
user.project_users
=> [#<User id: 2, name: "Kathleen">, #<User id: 3, name: "Anne">]

答案 2 :(得分:1)

由于评论主题有点费力,我会试着说出我是如何直接解决它的。

users = current_user.projects.map(&:users).flatten.uniq

答案 3 :(得分:0)

由于项目是聚合,它将返回Enumerable对象。您可以使用(例如)每个迭代它们:

current_user.projects.each {|prj|
  prj.users.each{|usr|
    #do stuff
  }
}

如果你想将这些收集到一个可枚举(并摆脱重复),那么

users_from_projects = current_user.projects.reduce([]) {|all_users,prj|
  all_users = all_users + prj.users
}.uniq

编辑纠正,谢谢你指出它。

答案 4 :(得分:0)

向您的用户类添加:

 has_many :users, :through => :permissions

所以它看起来像:

 class User < ActiveRecord::Base
   has_many :projects, :through => :permissions
   has_many :users, :through => :permissions

答案 5 :(得分:-1)

您需要eager-load your associations

class User < ActiveRecord::Base
  has_many :projects, :through => :permissions, :include => :users
  has_many :permissions, :include => {:project => :users}
  …
end