Rails 4 scope per trovare genitori senza figli

Ho trovato una risposta che aveva alcuni esempi utilizzabili per trovare genitori con figli, ma lo stesso non è utilizzabile per trovare genitori senza figli (presumibilmente dal momento che il join li esclude).

 scope :with_children, joins(:children).group("child_join_table.parent_id").having("count(child_join_table.parent_id) > 0") 

Qualcuno può indicarmi la giusta direzione?

Questo dovrebbe fare il lavoro che desideri:

Rotaie 3 e 4

 scope :without_children, includes(:children).where(:children => { :id => nil }) 

La grande differenza qui è che i joins diventano un includes : un include carica tutte le relazioni, se esistono, il join caricherà solo gli oggetti associati e ignorerà l’object senza una relazione.

In effetti, scope :with_children, joins(:children) dovrebbe essere sufficiente per restituire il Parent con almeno 1 figlio. Provalo!

Rails 5

Vedi la risposta di @ Anson qui sotto


Come ha sottolineato @MauroDias, se si tratta di una relazione autoreferenziale tra genitori e figli, questo codice non funzionerà.

Con un po ‘di ricerca, ho scoperto come farlo:

Considera questo modello:

 class Item < ActiveRecord::Base has_many :children, :class_name => 'Item', :foreign_key => 'parent_id' 

Come restituire tutti gli articoli senza figli (i):

 Item.includes(:children).where(children_items: { id: nil }) 

Come ho trovato il tavolo children_items ?

Item.joins(:children) genera il seguente SQL:

 SELECT "items".* FROM "items" INNER JOIN "items" "children_items" ON "children_items"."parent_id" = "items"."id" 

Quindi ho indovinato che Rails usa un tavolo quando ha bisogno di un JOIN in un caso autoreferenziale.


Domande simili:

  • Come interrogare un modello in base all’attributo di un altro modello che appartiene al primo modello?
  • Rota l’associazione di query di record triggers con “esiste”
  • Rails 3, has_one / has_many con condizione lambda
  • Partecipa a più tabelle con record attivi

@ Mikyahi ha una solida risposta di Rails 4, ma per le persone che arrivano qui con Rails 5 hai più opzioni.

Utilizzando Rails 5:

A partire da Rails 5, puoi anche utilizzare left_outer_joins per evitare di caricare l’associazione. È stato introdotto nella richiesta pull # 12071 .

 scope :without_children, left_outer_joins(:children).where(children: { id: nil }) 

Per i genitori con bambini, la soluzione Rails 4 di MrYoshiji è ancora quella da utilizzare:

 scope :with_children, joins(:children) 

Ecco come ho risolto per Rails 5:

 scope :without_comments, -> do left_outer_joins(:comments).where(comments: { id: nil }) end