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

发布于 2024-09-24 04:29:49 字数 1074 浏览 4 评论 0原文

您好,

我有一个应用程序,其中 CompaniesUser 需要通过 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,对吗?或者 Relation 比这更聪明吗?),我不确定是否有更好的方法来解决这个问题(也许使用范围或 Rails 3 使用的新奇的 Relation 对象?)。

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

Greetings,

I have an application where Companies and Users need to belong to each other through a CompanyMembership model, which contains extra information about the membership (specifically, whether or not the User is an admin of the company, via a boolean value admin). A simple version of the code:

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

Of course, this makes it simple to get all the members of a company via company.users.all, et al. However, I am trying to get a list of all Users in a Company who are admins of that Company (and also to test whether a user is an admin of a given company). My first solution was the following in company.rb:

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

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

While this works, something feels inefficient about it (it's iterating over each membership, executing SQL each time, right? Or is Relation smarter than that?), and I'm not sure if there's a better way to go about this (perhaps using scopes or the fancy new Relation objects that Rails 3 uses?).

Any advice on the best way to procede (preferably using Rails 3 best practices) would be greatly appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

是伱的 2024-10-01 04:29:49

我相信我以错误的方式处理这个问题,在 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 确保所有
加载指定的关联
使用尽可能少的数量
查询.查询。 RoR 指南:ActiveRecord 查询

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

I believe I was going about this the wrong way, specifying conditions on company_memberships instead of users, which was what I actually wanted (a list of Users, not a list of CompanyMemberships). The solution I think I was looking for is:

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

which generates the following SQL (for company with ID of 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')

I'm not sure yet if I'll need it, but the includes() method will perform eager loading to keep down the number of SQL queries if necessary:

Active Record lets you specify in
advance all the associations that are
going to be loaded. This is possible
by specifying the includes method of
the Model.find call. With includes,
Active Record ensures that all of the
specified associations are loaded
using the minimum possible number of
queries.queries. RoR Guides: ActiveRecord Querying

(I'm still open to any suggestions from anyone who thinks this isn't the best/most effective/right way to go about this.)

岁月打碎记忆 2024-10-01 04:29:49

一种更简洁的方法是将关联添加到您的公司模型中,如下所示:

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

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

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

An even cleaner way would be to add an association to your Company model, something like this:

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

You might have to dig into the rails doc to get the exact syntax right.

You shouldn't need :include, unless you have other classes associated with :user that you might reference in your view.

﹏雨一样淡蓝的深情 2024-10-01 04:29:49

像这样的事情怎么样:

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

How about something like this:

Company.find(:id).company_memberships.where(:admin => true).joins(:user)
深爱成瘾 2024-10-01 04:29:49

我偶然发现了这个答案,并相信现在有一种更好的方法使用 has_many 关联范围 (has_many 文档):

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

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

I stumbled across this answer and believe that nowadays there is a nicer way using has_many association scopes (has_many documentation):

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

The second parameter of a has_many association can be a proc or lambda that contains your filter.

别在捏我脸啦 2024-10-01 04:29:49

我制作了 activerecord_where_assoc gem 来执行此操作。

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

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

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

该 gem 在 Rails 3 中不起作用,但 9 年后我们支持 Rails 4.1 及更多版本。

以下是简介示例。请参阅文档了解更多详细信息。

I made the activerecord_where_assoc gem to do this.

With it, no need for joins or includes. Doing eager loading remains your choice, not an obligation.

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

The question is unclear on how to filter the company, so I added it myself. Without this, if a user can be in multiple company, it being admin in one company could mean being treated as an admin in another company.

The gem doesn't work in Rails 3, but we are 9 years later and Rails 4.1 and more are supported.

Here are the introduction and examples. Read more details in the documentation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文