我有以下结构,我正在尝试include
has_many :through
关联。如果我不预加载collections
,它可以正常工作,但我遇到了N+1
问题。
如何在选择products
时将父collections
关联的条件传递给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
使用:
Line.all.each{ |line| line.collections }
不能工作:
Line.includes(:collections).all.each{ |line| line.collections }
引发错误:
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):
答案 0 :(得分:0)
原来我只是个白痴。我所要做的就是在lambda中include(:products)
。下面是工作解决方案,并通过将collections
到products
选择的逻辑移动到:by_active_products
上的命名范围Collections
进一步进行了干预。这两种方法都适用于预加载,并且都不会导致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
立即工作:
Line.includes(:collections).all.each{ |line| line.collections }