2010-04-29 23 views
10

Non so perché non riesco a capirlo, penso che dovrebbe essere abbastanza semplice. Ho due modelli (vedi sotto). Sto cercando di creare uno scope con nome per SupplierCategory in grado di trovare tutte le categorie di fornitori (compresi i fornitori) i cui fornitori non sono vuoti.ActiveRecord trova tutti i genitori che hanno figli associati

Ho provato un dritto fino join, named_scope :with_suppliers, :joins => :suppliers che mi dà solo le categorie con i fornitori, ma mi dà ogni categoria elencati separatamente, quindi se una categoria ha 2 fornitori, ho la categoria due volte nella matrice restituita:

Attualmente sto usando:

named_scope :with_suppliers, :include => :suppliers 

e poi a mio avviso che sto usando:

<%= render :partial => 'category', :collection => @categories.find_all{|c| !c.suppliers.empty? } %> 

Non esattamente eloquente ma illustrat è quello che sto cercando di ottenere.

definizioni delle classi

class SupplierCategory < AR 
    has_many :suppliers, :order => "name" 
end 

class Supplier < AR 
    belongs_to :supplier 
end 
+0

Nella classe 'Subbplier' hai significato' appartiene_a: fornitore_categoria'? – klew

risposta

12

Ecco un altro approccio:

named_scope :with_suppliers, :include => :suppliers, 
          :conditions => "suppliers.id IS NOT NULL" 

Questo funziona perché Rails usa OUTER Crea include clausola. Quando non vengono trovate righe corrispondenti, la query restituisce valori NULL per le colonne del fornitore. Quindi il controllo NOT NULL restituisce le righe corrispondenti.

Rails 4

scope :with_suppliers, { includes(:steps).where("steps.id IS NOT NULL") } 

o utilizzando un metodo statico:

def self.with_suppliers 
    includes(:steps).where("steps.id IS NOT NULL") 
end 

Nota:

Questa soluzione carica desiderosi fornitori.

categories = SupplierCategory.with_suppliers 
categories.first.suppliers #loaded from memory 
+0

Sicuramente il più sintetico di tutte le soluzioni e funziona perfettamente !! Grazie – brad

+1

Aggiornato per le rotaie 4: 'ambito: in_use, -> {include (: gradini) .dove ("steps.id IS NOT NULL")}' e la sua controparte 'ambito: not_in_use, -> { include (: steps) .where ("steps.id IS NULL")} ' – soychicka

1

credo che sarebbe stato qualcosa di simile

#model SupplierCategory 
named_scope :with_suppliers, 
    :joins => :suppliers, 
    :select => "distinct(supplier_categories), supplier_categories.*", 
    :conditions => "suppliers.supplier_categories_id = supplier_categories.id" 

Fammi sapere se funziona per voi.

Edit: Utilizzando l'idea di fl00r:

named_scope :with_suppliers, 
    :joins => :suppliers, 
    :select => "distinct(supplier_categories), supplier_categories.*", 
    :having => "count(supliers.id) > 0" 

Credo che questo sia il modo più veloce.

+1

Sei sicuro di dover utilizzare forme singolari di modello fornitore_categoria anziché fornitore_categorie? – fl00r

+0

no = P tks per quello. –

3
class SupplierCategory < AR 
    has_many :supliers 

    def self.with_supliers 
    self.all.reject{ |c| c.supliers.empty? } 
    end 
end 

SupplierCategory.with_supliers 
#=> Array of SuplierCategories with supliers 

Un altro modo più flessibile utilizzando named_scope

class SupplierCategory < AR 
    has_many :supliers 
    named_scope :with_supliers, :joins => :supliers, :select => 'distinct(suplier_categories.id), suplier_categories.*', :having => "count(supliers.id) > 0" 
end 

SupplierCategory.with_supliers(:all, :limit => 4) 
#=> first 4 SupplierCategories with suppliers 
+0

Credo che l'uso di 'join' sia più veloce, non è vero? Non so se è rilevante nel suo caso ... ma sono curioso = P –

+0

sì, hai ragione, quindi ho aggiunto la soluzione con join – fl00r

+1

Il tuo codice restituirà dati duplicati a causa del join. –

3

versione più semplice:

named_scope :with_suppliers, :joins => :suppliers, :group => :id 

Se si desidera utilizzare frequentemente, è consigliabile utilizzare counter_cache.

+0

+1 conciso .... –

Problemi correlati