ActiveRecord, has_many: through e Polymorphic Associations


117

gente,

Voglio essere sicuro di averlo capito correttamente. E per favore ignora il caso dell'ereditarietà qui (SentientBeing), cercando invece di concentrarti sui modelli polimorfici in has_many: attraverso le relazioni. Detto questo, considera quanto segue ...

class Widget < ActiveRecord::Base
  has_many :widget_groupings

  has_many :people, :through => :widget_groupings, :source => :person, :conditions => "widget_groupings.grouper_type = 'Person'"
  has_many :aliens, :through => :widget_groupings, :source => :alien, :conditions => "video_groupings.grouper_type = 'Alien'"
end

class Person < ActiveRecord::Base
  has_many :widget_groupings, :as => grouper
  has_many :widgets, :through => :widget_groupings
end

class Alien < ActiveRecord::Base
  has_many :widget_groupings, :as => grouper
  has_many :widgets, :through => :widget_groupings  
end

class WidgetGrouping < ActiveRecord::Base
  belongs_to :widget
  belongs_to :grouper, :polymorphic => true
end

In un mondo perfetto, mi piacerebbe, dato un Widget e una Persona, fare qualcosa come:

widget.people << my_person

Tuttavia, quando lo faccio, ho notato che il "tipo" di "cernia" è sempre nullo in widget_groupings. Tuttavia, se faccio qualcosa di simile al seguente:

widget.widget_groupings << WidgetGrouping.new({:widget => self, :person => my_person}) 

Quindi tutto funziona come mi sarei normalmente aspettato. Non credo di aver mai visto questo verificarsi con associazioni non polimorfiche e volevo solo sapere se si trattava di qualcosa di specifico per questo caso d'uso o se sto potenzialmente fissando un bug.

Grazie per qualsiasi aiuto!

Risposte:


162

C'è un problema noto con Rails 3.1.1 che interrompe questa funzionalità. Se riscontri questo problema, prova prima ad eseguire l'aggiornamento, è stato risolto in 3.1.2

Sei così vicino. Il problema è che stai abusando dell'opzione: source. : source dovrebbe puntare alla relazione polimorfica appartiene_to. Quindi tutto ciò che devi fare è specificare: source_type per la relazione che stai cercando di definire.

Questa correzione al modello Widget dovrebbe consentirti di fare esattamente quello che stai cercando.

class Widget < ActiveRecord::Base
  has_many :widget_groupings

  has_many :people, :through => :widget_groupings, :source => :grouper, :source_type => 'Person'
  has_many :aliens, :through => :widget_groupings, :source => :grouper, :source_type => 'Alien'
end

Oh mio dio è così dolorosamente ovvio che non riesco a credere di esserci soffermato sopra. Grazie EmFi!
Cory

Nessun problema, penso di aver agonizzato per circa un giorno su come farlo la prima volta che l'ho incontrato. Non ha aiutato il fatto che fosse una delle prime cose che ho provato a fare in Rails che non comportava il seguire un tutorial / libro.
EmFi

1
Come sottolinea scotkf, c'è una regressione in ActiveRecord 3.1.1 che blocca questo comportamento. L'aggiornamento alla 3.1.2 consentirà a questa soluzione di funzionare.
EmFi

6
Stessa cosa di cui ha parlato @Shtirlic. C'è un modo per non specificare source_type, quindi hai un set di risultati misto? Se qualcuno risolvesse questo problema, mi piacerebbe sapere come.
Damon Aw

3
Funziona ancora a partire da Rails 4.2.0. Tuttavia, c'è un modo per farlo in questi giorni senza source_type e due associazioni separate?
Emeka

3

Come accennato in precedenza, questo non funziona con rails 3.1.1 a causa di un bug su: source, ma è stato risolto in Rails 3.1.2


-4

ne ha molti: attraverso e polimorfico non funzionano insieme. Se provi ad accedervi direttamente, dovrebbe generare un errore. Se non mi sbaglio, devi scrivere a mano widget.people e la routine push.

Non penso sia un bug, è solo qualcosa che non è stato ancora implementato. Immagino che lo vediamo nella funzionalità, perché tutti hanno un caso in cui potrebbero usarlo.


6
Lavorano insieme. Ad esempio: has_many: subscriptions,: as =>: subscribable has_many: subscribers,: through =>: subscriptions
,:

Lancerò un esempio del mio codice fallito come un post separato nel prossimo futuro :) Mi farebbe risparmiare un sacco di mal di testa capire come bypassare quell'errore.
cgr
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.