2017-10-24 1 views
2

Ich habe die folgende Struktur und versuche include eine has_many :through Assoziation. Es funktioniert ordnungsgemäß, wenn ich die collections nicht vorladen, aber dann bin ich mit einem N+1 Problem konfrontiert.Schienen 4 - Preloading has_many durch Zuordnung schlägt mit Lambda-Bedingungen von durch Tabelle

Wie kann ich Bedingungen der übergeordneten Zuordnung products in das Lambda übergeben, wenn Sie collections auswählen?

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 } 

funktioniert nicht:

Line.includes(:collections).all.each{ |line| line.collections } 

Fehler Würfe:

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): 

Antwort

0

Stellt sich heraus, ich bin nur ein Idiot. Alles, was ich tun musste, ist include(:products) im Lambda. Die Arbeitslösung ist unten und wurde weiter durch Bewegen der Logik zum Auswählen von collections bis products in den genannten Bereich :by_active_products auf Collections bewegt. Beide Methoden arbeiten mit dem Vorladen und verursachen kein N+1 Problem.

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 

Arbeiten jetzt:

Line.includes(:collections).all.each{ |line| line.collections } 
Verwandte Themen