Ruby on rails 当两列是同一表的外键时,3列之间的Rails唯一性

Ruby on rails 当两列是同一表的外键时,3列之间的Rails唯一性,ruby-on-rails,ruby,postgresql,unique-constraint,Ruby On Rails,Ruby,Postgresql,Unique Constraint,我有两个表,资产和关系。它们看起来像这样(加上为简洁起见我省略的其他列): 我希望两个资产之间的关系类型是唯一的。例如,假设我有一个成员关系类型 Relationship.create!(类型:'membership',资产编号:'61d58a49-86a9-4d7f-b069-2ed1fa27b387',资产编号:'1856df48-3193-45de-bef0-122cd9f58d7b') 如果我再次尝试创建该记录,我可以使用validates:type,university:{scope

我有两个表,资产和关系。它们看起来像这样(加上为简洁起见我省略的其他列):

我希望两个资产之间的关系类型是唯一的。例如,假设我有一个
成员关系类型

Relationship.create!(类型:'membership',资产编号:'61d58a49-86a9-4d7f-b069-2ed1fa27b387',资产编号:'1856df48-3193-45de-bef0-122cd9f58d7b')
如果我再次尝试创建该记录,我可以使用
validates:type,university:{scope:[:asset1\u id,:asset2\u id]}
add\u index:relationships,[:type,:asset1\u id,:asset2\u id],unique:true来阻止它,但是当我使用这些时,不会阻止以下情况

Relationship.create!(类型:'membership',资产编号:'1856df48-3193-45de-bef0-122cd9f58d7b',资产编号:'61d58a49-86a9-4d7f-b069-2ed1fa27b387')
请注意,这与上一条记录相同,只是资产ID的顺序颠倒了


如何防止这种情况(最好是在DB级别)?

如果要在DB级别验证,需要在联接表的两个字段上设置复合索引。更多信息可在此处找到:

假设调用联接表
成员身份
,请尝试以下迁移:

add_index :memberships, [:relationship_id, :asset_id], unique: true
或者,要让rails处理验证,请执行以下操作:

class Membership < ActionRecord::Base
  validates_uniqueness_of :relationship_id, scope: :membership_id
  ...
end
类成员资格
更多关于rails验证的阅读:

您可以通过添加自定义验证在应用程序级别执行此操作

validates :type, uniqueness: { scope: [:asset1_id, :asset2_id] }
validate :reverse_type_uniqueness

def reverse_type_uniqueness
  duplicate_present = self.class.where(type: type, asset1_id: asset2_id, asset2_id: asset1_id).exists?
  errors.add(:base, "Duplicate present") if duplicate_present?
end
要在DB级别实现双面唯一索引,下面是一个示例,但不是很直接


您提供的应用程序级别似乎不起作用,但DB链接看起来很有用。我会做一些研究,看看为博士后提供的类似解决方案是什么样子的。@dyeje你试过吗?
class Membership < ActionRecord::Base
  validates_uniqueness_of :relationship_id, scope: :membership_id
  ...
end
validates :type, uniqueness: { scope: [:asset1_id, :asset2_id] }
validate :reverse_type_uniqueness

def reverse_type_uniqueness
  duplicate_present = self.class.where(type: type, asset1_id: asset2_id, asset2_id: asset1_id).exists?
  errors.add(:base, "Duplicate present") if duplicate_present?
end