ActiveRecord, has_many: through et associations polymorphiques

117

Gens,

Je veux m'assurer de bien comprendre cela. Et s'il vous plaît, ignorez le cas de l'héritage ici (SentientBeing), en essayant de vous concentrer sur les modèles polymorphes dans has_many: through relations. Cela dit, considérez ce qui suit ...

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

Dans un monde parfait, j'aimerais, étant donné un widget et une personne, faire quelque chose comme:

widget.people << my_person

Cependant, lorsque je fais cela, j'ai remarqué que le «type» du «mérou» est toujours nul dans widget_groupings. Cependant, si je fais quelque chose comme ce qui suit:

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

Ensuite, tout fonctionne comme je l'aurais normalement prévu. Je ne pense pas avoir jamais vu cela se produire avec des associations non polymorphes et je voulais juste savoir si c'était quelque chose de spécifique à ce cas d'utilisation ou si je suis potentiellement en train de regarder un bogue.

Merci pour toute aide!

Cory
la source

Réponses:

162

Il existe un problème connu avec Rails 3.1.1 qui rompt cette fonctionnalité. Si vous rencontrez ce problème, essayez d'abord la mise à niveau, il a été corrigé dans la version 3.1.2

Tu es si proche. Le problème est que vous utilisez mal l'option: source. : la source doit pointer vers la relation polymorphe appartient_to. Il ne vous reste plus qu'à spécifier: source_type pour la relation que vous essayez de définir.

Cette correction du modèle de widget devrait vous permettre de faire exactement ce que vous recherchez.

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
la source
Oh mon dieu, c'est si douloureusement évident que je ne peux pas croire que je me suis glacé dessus. Merci EmFi!
Cory
Pas de problème, je pense que je me suis demandé pendant environ un jour comment faire cela la première fois que je l'ai rencontré. Cela n'a pas aidé que ce soit l'une des premières choses que j'ai essayé de faire dans Rails qui n'impliquait pas de suivre un tutoriel / livre.
EmFi
1
Comme le souligne scotkf, il existe une régression dans ActiveRecord 3.1.1 qui bloque ce comportement. La mise à niveau vers la version 3.1.2 permettra à cette solution de fonctionner.
EmFi
6
Même chose que ce que @Shtirlic a mentionné. Existe-t-il un moyen de ne pas spécifier source_type, de sorte que vous avez un ensemble de résultats mélangés? Si quelqu'un a résolu ce problème, j'aimerais savoir comment.
Damon Aw
3
Fonctionne toujours à partir de Rails 4.2.0. Cependant, y a-t-il un moyen d'accomplir cela ces jours-ci sans source_type et deux associations distinctes?
Emeka
3

Comme mentionné ci-dessus, cela ne fonctionne pas avec les rails 3.1.1 en raison d'un bogue sur: source, mais il est corrigé dans Rails 3.1.2

Scottkf
la source
-4

a beaucoup: à travers et polymorphe ne fonctionnent pas ensemble. Si vous essayez d'y accéder directement, cela devrait générer une erreur. Si je ne me trompe pas, vous devez écrire à la main widget.people et la routine push.

Je ne pense pas que ce soit un bogue, c'est juste quelque chose qui n'a pas encore été implémenté. J'imagine que nous le voyons dans la fonctionnalité, car tout le monde a un cas dans lequel il pourrait l'utiliser.

cgr
la source
6
Ils travaillent ensemble. Par exemple: has_many: abonnements,: as =>: abonné has_many: abonnés,: through =>: abonnements
,:
Je vais jeter un exemple de mon code défaillant dans un article séparé dans un proche avenir :) Cela me sauverait beaucoup de maux de tête pour comprendre comment contourner cette erreur.
cgr