Уникальность Rails между 3 столбцами, когда 2 столбца являются внешними ключами одной и той же таблицы - PullRequest
0 голосов
/ 24 февраля 2020

У меня есть две таблицы: активы и отношения. Они выглядят так (плюс другие столбцы, которые я опускаю для краткости):

# Table name: relationships
#
#  id             :uuid             not null, primary key
#  asset1_id      :uuid             not null
#  asset2_id      :uuid             not null
#  type           :string           not null
# Table name: assets
#
#  id             :uuid             not null, primary key
#  type           :string           not null
#  name           :string           not null

Я хочу, чтобы отношения типа между двумя активами были уникальными. Например, предположим, у меня есть отношение типа membership.

Relationship.create!(type: 'membership', asset1_id: '61d58a49-86a9-4d7f-b069-2ed1fa27b387', asset2_id: '1856df48-3193-45de-bef0-122cd9f58d7b')

. Если я попытаюсь создать эту запись снова, я могу легко предотвратить ее с помощью validates :type, uniqueness: { scope: [:asset1_id, :asset2_id] } и add_index :relationships, [:type, :asset1_id, :asset2_id], unique: true, однако когда Я использую их в следующем случае не предотвращено :

Relationship.create!(type: 'membership', asset1_id: '1856df48-3193-45de-bef0-122cd9f58d7b', asset2_id: '61d58a49-86a9-4d7f-b069-2ed1fa27b387')

Обратите внимание, что это то же самое, что и предыдущая запись, только с обратным порядком идентификаторов активов.

Как я могу предотвратить это (желательно на уровне БД)?

Ответы [ 2 ]

1 голос
/ 24 февраля 2020

Вы можете сделать это на уровне приложения, добавив пользовательскую проверку

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

Для реализации двухстороннего уникального индекса на уровне БД, вот пример, не очень простой, хотя

https://dba.stackexchange.com/questions/14109/two-sided-unique-index-for-two-columns

0 голосов
/ 24 февраля 2020

Если вы хотите проверить его на уровне БД, вам нужно настроить составной индекс в обоих полях таблицы соединений. Дополнительную информацию можно найти здесь: Как реализовать уникальный индекс для двух столбцов в рельсах

Предполагая, что вы вызываете свою таблицу соединения memberships, попробуйте следующую миграцию:

add_index :memberships, [:relationship_id, :asset_id], unique: true

В качестве альтернативы, чтобы рельсы могли обрабатывать проверку:

class Membership < ActionRecord::Base
  validates_uniqueness_of :relationship_id, scope: :membership_id
  ...
end

Дополнительные сведения о проверке рельсов: https://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of

...