Рельсы Полиморфные has_many - PullRequest
13 голосов
/ 09 июля 2010

Используя Ruby on Rails, как я могу получить полиморфное отношение has_many, когда владелец всегда известен, но элементы в ассоциации будут иметь некоторый полиморфный (но однородный) тип, указанный в столбце владельца ? Например, предположим, что продукты Producer class has_many, но экземпляры производителей могут на самом деле иметь много велосипедов, фруктовое мороженое или шнурки. Я легко могу иметь каждый класс продукта (Bicycle, Popsicle и т. Д.) Иметь отношение belongs_to к производителю, но с учетом экземпляра производителя, как я могу получить коллекцию продуктов, если они имеют различные типы (для экземпляра производителя)?

Рельсовые полиморфные ассоциации позволяют производителям принадлежать ко многим продуктам, но мне нужно, чтобы отношения были наоборот. Например:

class Bicycle < ActiveRecord::Base
  belongs_to :producer
end

class Popsicle < ActiveRecord::Base
  belongs_to :producer
end

class Producer < ActiveRecord::Base
  has_many :products, :polymorphic_column => :type # last part is made-up...
end

Таким образом, в моей таблице Producer уже есть столбец типа, который соответствует некоторому классу продуктов (например, Bicycle, Popsicle и т. Д.), Но как я могу получить Rails, чтобы позволить мне сделать что-то вроде:

>> bike_producer.products
#=> [Bicycle@123, Bicycle@456, ...]
>> popsicle_producer.products
#=> [Popsicle@321, Popsicle@654, ...]

Извините, если это очевидно или обычное повторение; У меня возникают удивительные трудности с легким достижением.

Ответы [ 5 ]

6 голосов
/ 28 сентября 2012

Вы должны использовать STI для производителей, а не для продуктов. Таким образом, у вас будет разное поведение для каждого типа производителя, но в одной таблице producers.

(почти) Полиморфизм отсутствует!

class Product < ActiveRecord::Base
  # does not have a 'type' column, so there is no STI here,
  # it is like an abstract superclass.
  belongs_to :producer
end

class Bicycle < Product
end

class Popsicle < Product
end

class Producer < ActiveRecord::Base
  # it has a 'type' column so we have STI here!!
end

class BicycleProducer < Producer
  has_many :products, :class_name => "Bicycle", :inverse_of => :producer
end

class PopsicleProducer < Producer
  has_many :products, :class_name => "Popsicle", :inverse_of => :producer
end
2 голосов
/ 10 июля 2010

пожалуйста, примите его в формате

class Bicycle < ActiveRecord::Base 
  belongs_to :bicycle_obj,:polymorphic => true 
end 

class Popsicle < ActiveRecord::Base
  belongs_to :popsicle_obj , :polymorphic => true 
end 

class Producer < ActiveRecord::Base 
  has_many :bicycles , :as=>:bicycle_obj 
  has_many :popsicle , :as=>:popsicle_obj 
end 

Используйте этот код.Если у вас есть какие-либо проблемы, пожалуйста, оставьте комментарий.

1 голос
/ 11 июля 2010

Вот обходной путь, который я сейчас использую. Он не предоставляет никаких удобных методов (операций сбора), которые вы получаете из реальных ActiveRecord :: Associations, но он предоставляет способ получить список продуктов для данного производителя:

class Bicycle < ActiveRecord::Base
  belongs_to :producer
end

class Popsicle < ActiveRecord::Base
  belongs_to :producer
end

class Producer < ActiveRecord::Base
  PRODUCT_TYPE_MAPPING = {
    'bicycle' => Bicycle,
    'popsicle' => Popsicle
  }.freeze
  def products
    klass = PRODUCT_TYPE_MAPPING[self.type]
    klass ? klass.find_all_by_producer_id(self.id) : []
  end
end

Другим недостатком является то, что я должен поддерживать отображение строк типов на классы типов, но это может быть автоматизировано. Однако этого решения будет достаточно для моих целей.

0 голосов
/ 15 февраля 2012

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

Ассоциация own_to может быть включена с помощью опции: polymorphic => true. Однако, если вы не используете наследование одной таблицы, ассоциация has_many не работает, потому что ей нужно знать набор таблиц, которые могут иметь внешний ключ.

(из того, что я нашел), я думаю, что чистое решение состоит в том, чтобы иметь таблицу и модель для базового класса и иметь внешний ключ в базовой таблице.

create_table "products", :force => true do |table|
    table.integer  "derived_product_id"
    table.string   "derived_product_type"
    table.integer  "producer_id"
  end

  class Product < ActiveRecord::Base
    belongs_to :producer
  end

  class Producer < ActiveRecord::Base
    has_many :products
  end

Затем для производственного объекта, производителя, вы должны получить продукты с product.products.derived_products.

Я еще не играл с has_many, чтобы сконденсировать ассоциацию с продюсером.

0 голосов
/ 09 июля 2010
class Note < ActiveRecord::Base

 belongs_to :note_obj, :polymorphic => true
 belongs_to :user


end


class Contact < ActiveRecord::Base

 belongs_to :contact_obj, :polymorphic => true
 belongs_to :phone_type 

end



class CarrierHq < ActiveRecord::Base


 has_many :contacts, :as => :contact_obj
 has_many :notes, :as => :note_obj


end
...