2017-10-24 13 views
2

나는 다음과 같은 구조를 가지고 includehas_many :through 연결하려고합니다. collections을 미리 탑재하지 않으면 올바르게 작동하지만 그 다음에 N+1 문제가 발생합니다.레일 4 - 사전로드 has_many 연결을 통해 테이블의 람다 조건과 함께 실패

collections을 선택할 때 부모의 조건을 람다에 products 연결을 어떻게 전달할 수 있습니까?

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

난 그냥 바보 밝혀졌습니다. 내가해야 할 일은 람다에 include(:products)입니다. 작업 해결책은 아래에 있으며, collections을 통해 products을 선택하는 로직을 에있는 명명 된 범위 :by_active_products으로 옮김으로써 추가로 DRY되었습니다. 두 가지 방법 모두 사전로드로 작동하며 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 }