【问题标题】:Filtering child objects in a has_many :through relationship in Rails 3在has_many中过滤子对象:通过Rails 3中的关系
【发布时间】:2011-04-14 19:04:55
【问题描述】:

您好,

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

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

【问题讨论】:

    标签: activerecord ruby-on-rails-3 has-many-through


    【解决方案1】:

    我相信我做错了,在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 Guides: ActiveRecord Querying

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • :conditionshas_many 的有效选项吗?它没有出现在文档中。
      • @CeasarBautista 这个答案似乎已经过时了。请参阅标题“自定义查询”下的文档,它告诉您使用 -&gt;
      【解决方案3】:

      这样的事情怎么样:

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

      【讨论】:

      • 又近了一步!虽然这不是我想要的答案,但它引导我找到了它:)
      【解决方案4】:

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

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

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

      【讨论】:

      • 这似乎是 2019 年的现代方式。您可以在 -&gt; 的前面添加一个参数,这将是您正在使用的模型的实例,允许您参考它在where
      【解决方案5】:

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

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

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

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

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

      这里是introductionexamples。在documentation 中阅读更多详细信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-27
        • 2011-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-06
        • 1970-01-01
        相关资源
        最近更新 更多