ActiveRecord, has_many: through i Skojarzenia polimorficzne

117

Ludzie,

Chcę się upewnić, że dobrze to rozumiem. I proszę zignoruj ​​tutaj przypadek dziedziczenia (SentientBeing), próbując zamiast tego skupić się na modelach polimorficznych w has_many: poprzez relacje. To powiedziawszy, rozważ następujące ...

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

W idealnym świecie chciałbym, mając widżet i osobę, zrobić coś takiego:

widget.people << my_person

Jednak kiedy to robię, zauważyłem, że 'typ' 'groupera' jest zawsze pusty w widget_groupings. Jeśli jednak dojdę do czegoś takiego:

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

Wtedy wszystko działa tak, jak bym się normalnie spodziewał. Nie sądzę, żebym kiedykolwiek widział, jak to się dzieje w przypadku skojarzeń niepolimorficznych i po prostu chciałem wiedzieć, czy było to coś specyficznego dla tego przypadku użycia, czy też potencjalnie wpatruję się w błąd.

Dzięki za pomoc!

Cory
źródło

Odpowiedzi:

162

W Railsach 3.1.1 występuje znany problem, który przerywa tę funkcjonalność. Jeśli masz ten problem, spróbuj najpierw zaktualizować, został on naprawiony w wersji 3.1.2

Jesteś tak blisko. Problem polega na tym, że nadużywasz opcji: source. : źródło powinno wskazywać na polimorficzną zależność should_to. Następnie wszystko, co musisz zrobić, to określić: typ_źródła dla relacji, którą próbujesz zdefiniować.

Ta poprawka do modelu widżetu powinna umożliwić Ci zrobienie dokładnie tego, czego szukasz.

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
EmFi
źródło
O mój Boże, to jest tak boleśnie oczywiste, że nie mogę uwierzyć, że przeszedłem tuż nad tym. Dzięki EmFi!
Cory
Nie ma problemu, myślę, że przez około jeden dzień męczyłem się nad tym, jak to zrobić, gdy pierwszy raz go spotkałem. Nie pomogło, że była to jedna z pierwszych rzeczy, które próbowałem zrobić w Railsach, które nie wymagały podążania za samouczkiem / książką.
EmFi
1
Jak wskazuje scotkf, w ActiveRecord 3.1.1 występuje regresja, która blokuje to zachowanie. Aktualizacja do wersji 3.1.2 umożliwi działanie tego rozwiązania.
EmFi
6
To samo, o czym wspomniał @Shtirlic. Czy istnieje sposób, aby nie określać source_type, więc masz mieszany zestaw wyników? Gdyby ktoś to rozwiązał, chciałby wiedzieć, jak to zrobić.
Damon Aw,
3
Nadal działa od Railsów 4.2.0. Czy jest jednak jakikolwiek sposób na osiągnięcie tego w dzisiejszych czasach bez source_type i dwóch oddzielnych skojarzeń?
Emeka
3

Jak wspomniano powyżej, nie działa to z railsami 3.1.1 z powodu błędu w: source, ale zostało to naprawione w Railsach 3.1.2

scottkf
źródło
-4

ma wiele: na wskroś i polimorficzne nie działają razem. Jeśli spróbujesz uzyskać do nich bezpośredni dostęp, powinien wyświetlić błąd. Jeśli się nie mylę, musisz ręcznie napisać widget.people i procedurę push.

Nie sądzę, żeby to był błąd, to po prostu coś, czego jeszcze nie zaimplementowano. Wyobrażam sobie, że widzimy to w funkcji, ponieważ każdy ma przypadek, w którym mógłby go użyć.

cgr
źródło
6
Pracują razem. Na przykład: has_many: subscriptions,: as =>: subscribable has_many: subscribers
,:
W najbliższej przyszłości podam w osobnym poście przykład mojego błędnego kodu :) Oszczędziłoby mi to wielu kłopotów z wymyśleniem, jak obejść ten błąd.
cgr