我有一个与其他两个模型具有has_and_belongs_to_many
关系的模型。例如,我有一个具有收件人的Message
模型。但是,收件人可以是两种不同类型的模型之一。例如InternalContact
和ExternalContact
。
建立关系很容易,我可以在其中获得消息的外部联系人(Message.first.external_contacts
)或内部联系人(Message.first.internal_contacts
)。然后,我可以同样地轻松地添加到此关系数组(Message.first.external_contacts << ExternalContact.first
)
我想要的是能够通过调用诸如以下内容来简化事物并使之更有效的功能:
Message.first.recipients # an array of both external and internal contacts
Message.first.recipients << ExternalContact/InternalContact # (add either model)
任何方向都值得赞赏,我肯定被困住了!
答案 0 :(得分:1)
您可以有一个单独的收件人类型表,然后仅与收件人映射邮件
Recipient Recipient_type Message Message_recipient_map
---------- --------------- ------- -------------------
id id id message_id
recipient_type_id recipient_id
然后,您可以在邮件和收件人之间具有has_many_through关系,并可以通过调用message.recipients
来访问邮件的所有收件人。希望这对您有帮助!
答案 1 :(得分:1)
使用显式的“具有并属于多个”(也称为has_many through:
)关系模型,您可以更好地控制该关系,并使它的一端变为多态:
class Message < ApplicationRecord
has_many :message_recipients
has_many :internal_recipients, through: :message_recipients, source: :recipient, source_type: "InternalContact"
end
class InternalContact < ApplicationRecord
has_many :message_recipients, as: :recipient
has_many :messages, through: :message_recipients, inverse_of: :internal_recipients
end
class MessageRecipient < ApplicationRecord
belongs_to :message
belongs_to :recipient, polymorphic: true
end
msg = Message.create!
rec = InternalContact.create!
msg.internal_recipients << rec
msg.message_recipients.each{|mr|
mr.recipient # here can be any model
}
答案 2 :(得分:1)
如果要避免多态关联,执行此操作的一种方法是使用Single Table Inheritance。
要执行此操作的一个主要原因是能够使用外键来确保引用完整性,而多态性是不可能的。
由于类型共享一个表,因此在订购时也可以将它们视为同类集合。
但是单表继承仅在不同类型本质上非常相似且可互换时才真正适用。
首先创建一个contacts
表,您将在其中存储所有不同的类型。
class CreateContacts < ActiveRecord::Migration[5.2]
def change
create_table :contacts do |t|
t.string :type
# and whatever columns you need
t.string :email
# ...
t.timestamps
end
end
end
请注意type
列。 ActiveRecord将使用它来存储子类的类名称。然后设置InternalContact和ExternalContact以从基Contact
类继承:
class InternalContact < Contact
# ...
end
class ExternalContact < Contact
# ...
end
这使您可以为每种子类型实现所需的任何逻辑(例如验证和回调)。
从联接表开始:
class CreateMessageRecipients < ActiveRecord::Migration[5.2]
def change
create_table :message_recipients do |t|
t.belongs_to :message
t.belongs_to :recipient, foreign_key: { to_table: :contacts }
t.timestamps
end
end
end
让我们使用has_many through:
和一个联接模型(而不是has_and_belongs_to_many
)来建立关联,这是非常有限的:
class Contact < ApplicationRecord
has_many :message_recipients, foreign_key: :recipient_id
has_many :messages, through: :message_recipients
end
class MessageRecipient < ApplicationRecord
belongs_to :message
belongs_to :recipient, class_name: 'Contact'
end
class Message < ApplicationRecord
has_many :message_recipients
has_many :recipients, through: :message_recipients
end