Ruby on rails 未能通过与联接表的关联立即加载作用域

Ruby on rails 未能通过与联接表的关联立即加载作用域,ruby-on-rails,ruby-on-rails-4,Ruby On Rails,Ruby On Rails 4,我使用联接表account\u category在帐户和类别之间建立了多对多关系。在帐户类中设置默认关系很容易: has_many :account_category has_many :categories, :through => :account_category 我搞不懂的是如何正确地界定额外的自定义类别 has_many :foobar_categories, -> { where type: "foobar" }, through

我使用联接表account\u category在帐户和类别之间建立了多对多关系。在
帐户
类中设置默认关系很容易:

has_many :account_category  
has_many :categories, :through => :account_category
我搞不懂的是如何正确地界定额外的自定义
类别

has_many :foobar_categories,
         -> { where type: "foobar" },
         through: :account_category,
         source: :category
这会导致加载foobar_类别失败:

# This is a N + 1 query 
Account.includes(:foobar_categories).all.each do
  # do stuff with account.foobar_categories
end
有人知道我可能做错了什么吗?是否真的不可能通过关联来加载作用域为多的对象

编辑:到目前为止,有两个回答建议在类别上使用范围。我不认为类别的范围是正确的,原因有二:

  • 在“关联的即时加载”部分中,它显示了在范围关联中这应该是可能的

  • 即使要使用范围,您如何加载关联的范围

  • 编辑2:我终于找到了问题的根源。方法调用隐藏在类中,与重写关联的我的关联同名。为我的浏览网站道歉,并感谢那些花时间回答的人。我将这篇文章标记为删除,因为错误在于我们的代码而不是Rails。

    我建议使用范围而不是自定义关联


    范围:foobar_categories->{includes(:categories)。其中(名称:'foobar')}

    在模型上定义范围:

    class Category < ActiveRecord::Base
      def self.foobar
        where("categories.name = 'foobar'")
      end
    end
    

    当您有多个要加载的自定义类别时,这就不太管用了。在我的例子中,实际上有4个类似的自定义关联我需要急切地加载并稍后访问。此外,我认为您不能急切地加载关联的范围。您将如何从对Account的调用中加载它。这样,只要您想访问某个帐户的foobar_类别,对集合的迭代就不会导致对DB的额外调用?这忽略了对关联本身的需要。我不想要所有foobar_类别,我想要一个特定帐户的所有foobar_类别,一个特定帐户的所有foobar2_类别,等等。然后将范围移动到该类别,然后调用像
    account.categories.foobar
    (请参见编辑以回答)如何加载范围?这是否仍然会导致每次调用account.categories.foobar时都调用db?此外,我认为最好按照jamesy829在
    define\u singleton\u方法
    的元编程中所建议的那样设置一个范围,这将进行一次查询,以获得每个帐户的
    类别
    。如果您不想这样做,请提前加载<代码>帐户.all.包括(:categories)。每个do Account.categories.foobar end
    。你最终会写4次完全相同的范围;这是元编程的主要好处之一。这仍然会导致N+1查询
    Account.all.includes(:categories)。每个{| Account | Account.categories.foobar_categories}
    都会为每个帐户生成一个额外的DB调用,因为DB必须进行一个新调用以确定作用域是什么。Includes仅适用于对categories的初始调用,但作用域中的where子句意味着rails需要额外的DB调用。编辑:为了明确起见:我测试了这一点,以确定情况确实如此。
    Account.includes(:categories).merge(Category.foobar).each do |a|
      a.categories.each do |c|
         puts c.created_at
      end
    end