ActiveRecord, has_many: through и полиморфные ассоциации

117

Folks,

Хочу убедиться, что я правильно это понимаю. И, пожалуйста, не обращайте внимания на наследование здесь (SentientBeing), пытаясь вместо этого сосредоточиться на полиморфных моделях в has_many: через отношения. Тем не менее, рассмотрите следующее ...

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

В идеальном мире я бы хотел сделать что-то вроде виджета и человека:

widget.people << my_person

Однако, когда я это делаю, я заметил, что «тип» группировщика всегда равен нулю в widget_groupings. Однако, если мне нужно что-то вроде следующего:

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

Тогда все работает так, как я обычно ожидал. Я не думаю, что когда-либо видел, как это происходит с неполиморфными ассоциациями, и просто хотел знать, было ли это чем-то особенным для этого варианта использования или я потенциально смотрю на ошибку.

Спасибо за любую помощь!

Cory
источник

Ответы:

162

В Rails 3.1.1 есть известная проблема, которая нарушает эту функциональность. Если у вас возникла эта проблема, попробуйте сначала выполнить обновление, она исправлена ​​в версии 3.1.2.

Ты так близко. Проблема в том, что вы неправильно используете параметр: source. : source должен указывать на полиморфное отношение own_to. Затем все, что вам нужно сделать, это указать: source_type для отношения, которое вы пытаетесь определить.

Это исправление модели виджетов должно позволить вам делать именно то, что вы ищете.

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
источник
Боже мой, это так до боли очевидно, что я не могу поверить, что покрыл это глазурью. Спасибо, EmFi!
Кори
Нет проблем, я думаю, что около дня мучился над тем, как это сделать, когда впервые столкнулся с этим. Не помогло то, что это была одна из первых вещей, которые я попытался сделать в Rails, не следуя руководству / книге.
EmFi
1
Как указывает scotkf, в ActiveRecord 3.1.1 есть регресс, который блокирует такое поведение. Обновление до 3.1.2 позволит этому решению работать.
EmFi 05
6
То же, что упомянул @Shtirlic. Есть ли способ не указывать source_type, чтобы получить смешанный набор результатов? Если бы кто-нибудь решил это, хотелось бы знать, как это сделать.
Дэймон Ав
3
По-прежнему работает с Rails 4.2.0. Однако есть ли способ добиться этого в наши дни без source_type и двух отдельных ассоциаций?
Emeka
3

Как упоминалось выше, это не работает с rails 3.1.1 из-за ошибки: source, но это исправлено в Rails 3.1.2.

scottkf
источник
-4

имеет много: сквозной и полиморфный не работают вместе. Если вы попытаетесь получить к ним доступ напрямую, это должно вызвать ошибку. Если я не ошибаюсь, вам нужно вручную написать widget.people и процедуру push.

Я не думаю, что это ошибка, это просто то, что еще не реализовано. Я могу представить, что мы видим это в этой функции, потому что у каждого есть кейс, в котором они могут ее использовать.

CGR
источник
6
Они действительно работают вместе. Например: has_many: subscriptions,: as =>: subscribable has_many: subscribers
,:
В ближайшем будущем я приведу пример своего кода с ошибкой в ​​виде отдельного сообщения :) Это избавило бы меня от головной боли, если бы я понял, как обойти эту ошибку.
cgr