20

问候,

我有一个应用程序需要通过模型相互归属,该Companies模型包含有关成员资格的额外信息(特别是,用户是否是公司的管理员,通过布尔值)。代码的简单版本:UsersCompanyMembershipadmin

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, et al 获取公司的所有成员变得简单。但是,我正在尝试获取公司中作为该公司管理员的所有用户的列表(并且还测试用户是否是给定公司的管理员)。我的第一个解决方案如下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,对吗?或者 Relation 比这更聪明?),我不确定是否有更好的方法来解决这个问题(也许使用范围或 Rails 3 使用的花哨的新Relation对象?)。

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

4

5 回答 5

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。通过包含,Active Record 确保使用尽可能少的 query.queries 加载所有指定的关联。RoR 指南:ActiveRecord 查询

(我仍然愿意接受任何认为这不是解决此问题的最佳/最有效/正确方法的人的任何建议。)

于 2010-09-21T14:56:59.590 回答
8

一种更简洁的方法是向您的 Company 模型添加关联,如下所示:

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

您可能必须深入研究rails doc才能获得正确的语法。

您不需要 :include,除非您有其他与 :user 关联的类,您可能会在视图中引用这些类。

于 2010-09-28T00:42:39.797 回答
4

像这样的东西怎么样:

Company.find(:id).company_memberships.where(:admin => true).joins(:user)
于 2010-09-20T20:53:06.687 回答
3

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

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

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

于 2019-02-14T18:38:35.607 回答
0

我制作了activerecord_where_assoc gem 来做到这一点。

有了它,无需加入或包含。进行急切加载仍然是您的选择,而不是义务。

users.where_assoc_exists(:company_memberships, admin: true, company_id: @company.id)

关于如何过滤公司的问题不清楚,所以我自己添加了它。没有这个,如果一个用户可以在多个公司,它是一个公司的管理员可能意味着被视为另一家公司的管理员。

该 gem 在 Rails 3 中不起作用,但我们已经过了 9 年,并且支持 Rails 4.1 及更高版本。

下面是介绍例子。阅读文档中的更多详细信息。

于 2020-02-25T13:31:36.280 回答