ActiveRecord, has_many: through e Polymorphic Associations


117

Pessoal,

Quero ter certeza de que entendi isso corretamente. E, por favor, desconsidere o caso de herança aqui (SentientBeing), tentando se concentrar em modelos polimórficos em has_many: por meio de relacionamentos. Dito isso, considere o seguinte ...

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

Em um mundo perfeito, gostaria de, com um widget e uma pessoa, fazer algo como:

widget.people << my_person

No entanto, quando faço isso, percebi que o 'tipo' de 'garoupa' é sempre nulo em widget_groupings. No entanto, se eu quiser algo como o seguinte:

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

Então tudo funciona como eu normalmente esperava. Acho que nunca vi isso ocorrer com associações não polimórficas e só queria saber se isso era algo específico para este caso de uso ou se estou potencialmente diante de um bug.

Obrigado por qualquer ajuda!

Respostas:


162

Existe um problema conhecido com o Rails 3.1.1 que quebra essa funcionalidade. Se você está tendo este problema, primeiro tente atualizar, ele foi corrigido no 3.1.2

Você está tão perto. O problema é que você está usando incorretamente a opção: source. : a origem deve apontar para o relacionamento polimórfico belongs_to. Então tudo que você precisa fazer é especificar: source_type para o relacionamento que você está tentando definir.

Essa correção para o modelo do widget deve permitir que você faça exatamente o que está procurando.

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 meu Deus, isso é tão dolorosamente óbvio que não posso acreditar que olhei para trás. Obrigado EmFi!
— Cory

Sem problemas, acho que agonizei por cerca de um dia pensando em como fazer isso na primeira vez que o encontrei. Não ajudou o fato de que foi uma das primeiras coisas que tentei fazer no Rails que não envolvia seguir um tutorial / livro.
— EmFi

1
Como scotkf aponta, há uma regressão no ActiveRecord 3.1.1 que bloqueia esse comportamento. A atualização para 3.1.2 permitirá que esta solução funcione.
— EmFi

6
Mesma coisa que @Shtirlic mencionou. Existe uma maneira de não especificar source_type, então você tem um conjunto de resultados mistos? Se alguém resolveu isso, adoraria saber como.
— Damon Aw,

3
Ainda funciona a partir do Rails 4.2.0. No entanto, existe alguma maneira de fazer isso atualmente sem source_type e duas associações separadas?
— Emeka

3

Como mencionado acima, isso não funciona com o Rails 3.1.1 devido a um bug no: source, mas foi corrigido no Rails 3.1.2


-4

tem muitos: through e polymorphic não funcionam juntos. Se você tentar acessá-los diretamente, ocorrerá um erro. Se não me engano, você deve escrever widget.people e a rotina push.

Não acho que seja um bug, é apenas algo que ainda não foi implementado. Eu imagino que vejamos no recurso, porque todo mundo tem um caso em que poderia usá-lo.


6
Eles trabalham juntos. Por exemplo: has_many: subscriptions,: as =>: subscribable has_many: subscribers,: through =>: subscriptions
— ,:

Vou apresentar um exemplo do meu código com falha em um post separado em um futuro próximo :) Me pouparia muita dor de cabeça descobrir como contornar esse erro.
— cgr
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.