[英]Rails 4 - Preloading has_many through association fails with lambda conditions of through table
I have the following structure and am trying to include
a has_many :through
association. 我具有以下结构,并尝试include
has_many :through
关联。 It works properly if I don't preload the collections
, but then I'm faced with an N+1
problem. 如果我不预加载collections
,它就可以正常工作,但是随后我遇到了N+1
问题。
How can I pass conditions of the parent products
association into the lambda when selecting collections
? 选择collections
时,如何将父products
关联的条件传递给lambda?
class Collection < ActiveRecord::Base
# == Schema Information
#
# Table name: products
# name :string(255) not null
has_many :products,
inverse_of: :collection
has_many :product_lines,
through: :products,
inverse_of: :products
has_many :lines,
through: :product_lines,
inverse_of: :product_lines
end
class Product < ActiveRecord::Base
# == Schema Information
#
# Table name: products
# active :boolean default(TRUE), not null
belongs_to :collection,
inverse_of: :products
has_many :product_lines,
inverse_of: :product
has_many :lines,
through: :product_lines,
inverse_of: :product_lines
end
class ProductLine < ActiveRecord::Base
belongs_to :product,
inverse_of: :product_lines
belongs_to :line,
inverse_of: :product_lines
end
class Line < ActiveRecord::Base
has_many :product_lines,
inverse_of: :line
has_many :products,
through: :product_lines,
inverse_of: :product_lines
# Gets Collections through Products where `product.active = true`
# And orders the Collections by `collection.name`
has_many :collections,
-> { where( products: {active: true} ).order(name: :ASC) },
through: :products,
inverse_of: :products
end
Works: 作品:
Line.all.each{ |line| line.collections }
Doesn't work: 不起作用:
Line.includes(:collections).all.each{ |line| line.collections }
Throws error: 引发错误:
ActiveRecord::StatementInvalid - PG::UndefinedTable: ERROR: missing FROM-clause entry for table "products"
LINE 1: SELECT "collections".* FROM "collections" WHERE "products"."...
^
: SELECT "collections".* FROM "collections" WHERE "products"."active" = $1 AND "collections"."id" IN (11, 30, 27, 12, 10, 13, 6, 4, 2, 7, 15, 9, 19, 1, 14, 8, 31, 5, 3, 29, 20, 17, 16, 37, 38, 41, 42, 43, 18, 44, 45, 26, 24, 25, 21, 22, 23):
Turns out I'm just an idiot. 原来我只是个白痴。 All I had to do is include(:products)
in the lambda. 我要做的就是在lambda中include(:products)
。 The working solution is below, and was further DRYed up by moving the logic for selecting collections
through products
into the named scope :by_active_products
on Collections
. 工作解决方案在下面,并且通过将用于通过products
选择collections
的逻辑移动到命名作用域:by_active_products
on Collections
来进一步干燥。 Both methods work with preloading and neither causes an N+1
issue. 两种方法都可以进行预加载,并且都不会导致N+1
问题。
class Collection < ActiveRecord::Base
# == Schema Information
#
# Table name: products
# name :string(255) not null
has_many :products,
inverse_of: :collection
has_many :product_lines,
through: :products,
inverse_of: :products
has_many :lines,
through: :product_lines,
inverse_of: :product_lines
scope :by_active_products, -> () {
includes(:products)
.where({products: {active: true}})
.order(name: :ASC)
.uniq
}
end
class Product < ActiveRecord::Base
# == Schema Information
#
# Table name: products
# active :boolean default(TRUE), not null
belongs_to :collection,
inverse_of: :products
has_many :product_lines,
inverse_of: :product
has_many :lines,
through: :product_lines,
inverse_of: :product_lines
end
class ProductLine < ActiveRecord::Base
belongs_to :product,
inverse_of: :product_lines
belongs_to :line,
inverse_of: :product_lines
end
class Line < ActiveRecord::Base
has_many :product_lines,
inverse_of: :line
has_many :products,
through: :product_lines,
inverse_of: :product_lines
# Gets Collections through Products where `product.active = true`
# And orders the Collections by `collection.name`
has_many :collections,
# Working using a named scope in the Collection model
-> { Collection.by_active_products },
# Also working; Query params directly in the lambda
# -> { includes(:products).where(products: {active: true}).order(name: :ASC).uniq },
through: :products,
inverse_of: :products
end
Working now: 现在工作:
Line.includes(:collections).all.each{ |line| line.collections }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.