多重有并属于许多关系

时间:2019-04-15 17:30:33

标签: ruby-on-rails ruby postgresql activerecord

我有一个与其他两个模型具有has_and_belongs_to_many关系的模型。例如,我有一个具有收件人的Message模型。但是,收件人可以是两种不同类型的模型之一。例如InternalContactExternalContact

建立关系很容易,我可以在其中获得消息的外部联系人(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)

任何方向都值得赞赏,我肯定被困住了!

3 个答案:

答案 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

要执行此操作的一个主要原因是能够使用外键来确保引用完整性,而多态性是不可能的。

由于类型共享一个表,因此在订购时也可以将它们视为同类集合。

但是单表继承仅在不同类型本质上非常相似且可互换时才真正适用。

设置STI

首先创建一个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