Rails 4 - 通过关联预加载 has_many 失败,lambda 条件为通过 table
Rails 4 - Preloading has_many through association fails with lambda conditions of through table
我有以下结构,正在尝试 include
一个 has_many :through
关联。如果我不预加载 collections
,它可以正常工作,但随后我会遇到 N+1
问题。
如何在选择 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
作品:
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" = 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):
原来我只是个白痴。我所要做的就是在 lambda 中 include(:products)
。工作解决方案在下面,并通过将选择 collections
到 products
的逻辑移动到 Collections
上的命名范围 :by_active_products
中进一步干燥。这两种方法都适用于预加载,都不会导致 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 }
我有以下结构,正在尝试 include
一个 has_many :through
关联。如果我不预加载 collections
,它可以正常工作,但随后我会遇到 N+1
问题。
如何在选择 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
作品:
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" = 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):
原来我只是个白痴。我所要做的就是在 lambda 中 include(:products)
。工作解决方案在下面,并通过将选择 collections
到 products
的逻辑移动到 Collections
上的命名范围 :by_active_products
中进一步干燥。这两种方法都适用于预加载,都不会导致 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 }