Rails Polymorphic has_many
Использование Ruby on Rails, как я могу достичь полиморфного отношения has_many
, где владелец всегда известен, но элементы в ассоциации будут иметь некоторый полиморфный (но гомогенный) тип, указанный столбцом у владельца? Например, предположим, что продукты Producer
class has_many
, но экземпляры производителей действительно могут иметь много велосипедов, или Popsicles, или Shoelaces. Я могу легко обеспечить, чтобы каждый класс продукта (Bicycle, Popsicle и т.д.) Имел отношение belongs_to
к Продюсеру, но дал экземпляр производителя, как я могу получить коллекцию продуктов, если они имеют разные типы (для экземпляра производителя)?
Полиморфные ассоциации Rails позволяют производителям относиться ко многим продуктам, но мне нужно, чтобы отношения были наоборот. Например:
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
#=> [[email protected], [email protected], ...]
>> popsicle_producer.products
#=> [[email protected], [email protected], ...]
Извините, если это очевидно или общее повторение; У меня есть удивительное затруднение в достижении этого легко.
Ответы
Ответ 1
Вот обходной путь, который я сейчас использую. Он не предоставляет никаких удобных методов (сборочных операций), которые вы получаете из реальных 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
Другой недостаток заключается в том, что я должен поддерживать сопоставление типов строк для ввода классов, но это может быть автоматизировано. Однако этого решения будет достаточно для моих целей.
Ответ 2
Вы должны использовать 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
Ответ 3
пожалуйста, возьмите его в формате
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
Используйте этот код. Если у вас возникли проблемы с этим, оставьте комментарий.
Ответ 4
Я обнаружил, что полиморфные ассоциации описаны в Rails.
Существует единая схема наследования таблицы, которая является самой большой документацией,
но если вы не используете однонаправленное наследование таблицы, то есть некоторая недостающая информация.
Ассоциацию belongs_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
Затем, для объекта Production, продюсера, вы должны получить продукты с продуктами производителя .products.derived_products.
Я еще не играл с has_many, чтобы сконденсировать ассоциацию с производителем .derived_products, поэтому я не могу комментировать, как это работает.
Ответ 5
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