在Rails 3中的has_many:through关系中过滤子对象

问题描述 投票:15回答:4

问候,

我有一个应用程序,CompaniesUsers需要通过CompanyMembership模型相互拥有,该模型包含有关成员资格的额外信息(具体来说,用户是否是公司的管理员,通过布尔值admin)。一个简单的代码版本:

class CompanyMembership < ActiveRecord::Base
  belongs_to :company
  belongs_to :user
end

class Company < ActiveRecord::Base
  has_many :company_memberships
  has_many :users, :through => :company_memberships
end

class User < ActiveRecord::Base
  has_many :company_memberships
  has_many :companies, :through => :company_memberships
end

当然,这使得通过company.users.all等人获得公司所有成员变得简单。但是,我正在尝试获取公司中所有用户的列表,这些用户是该公司的管理员(并且还要测试用户是否是给定公司的管理员)。我的第一个解决方案是company.rb中的以下内容:

def admins
  company_memberships.where(:admin => true).collect do |membership|
    membership.user
  end
end

def is_admin?(user)
    admins.include? user
end

虽然这有效,但它的效率却很低(它会迭代每个成员资格,每次都执行SQL,对吗?或者是比这更聪明的关系吗?),我不确定是否有更好的方法来解决这个问题(可能使用范围或Rails 3使用的奇特的新Relation对象?)。

任何关于最佳处理方式的建议(最好使用Rails 3最佳实践)将不胜感激!

activerecord ruby-on-rails-3 has-many-through
4个回答
17
投票

我相信我这是错误的方式,指定company_memberships而不是users的条件,这是我真正想要的(Users列表,而不是CompanyMemberships列表)。我认为我正在寻找的解决方案是:

users.where(:company_memberships => {:admin => true})

生成以下SQL(对于ID为1的公司):

SELECT "users".* FROM "users"
  INNER JOIN "company_memberships"
    ON "users".id = "company_memberships".user_id
  WHERE (("company_memberships".company_id = 1))
    AND ("company_memberships"."admin" = 't')

我不确定我是否需要它,但includes()方法将执行急切加载以在必要时保持SQL查询的数量:

Active Record允许您事先指定要加载的所有关联。这可以通过指定includes调用的Model.find方法来实现。使用includes,Active Record可确保使用尽可能少的queries.queries加载所有指定的关联。 RoR Guides: ActiveRecord Querying

(对于那些认为这不是最好/最有效/最正确的方法的人,我仍然愿意接受任何建议。)


7
投票

更简洁的方法是在公司模型中添加关联,如下所示:

has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true}

您可能需要深入了解rails doc以获得正确的语法。

您不应该:include,除非您有其他类与您关联:您可能在视图中引用的用户。


3
投票

这样的事情怎么样:

Company.find(:id).company_memberships.where(:admin => true).joins(:user)

1
投票

我偶然发现了这个答案,并相信现在使用has_many association scopeshas_many documentation)有一个更好的方法:

has_many :admins, -> { where(admin: true) }, through: :company_memberships, class_name: :user

has_many关联的第二个参数可以是包含过滤器的proc或lambda。

© www.soinside.com 2019 - 2024. All rights reserved.