Подтвердить что ты не робот

HABTM - ограничение уникальности

У меня две модели с отношением HABTM - Пользователь и Роль.

  • пользователь - has_and_belongs_to_many: роли
  • role - принадлежит_to: пользователь

Я хочу добавить ограничение уникальности в join (таблица users_roles), в котором указано, что user_id и role_id должны быть уникальными. В Rails будет выглядеть так:

validates_uniqueness_of :user, :scope => [:role]

Конечно, в Rails у нас обычно нет модели для представления отношения соединения в ассоциации HABTM.

Итак, мой вопрос: где лучшее место для добавления ограничения?

4b9b3361

Ответ 1

Вы можете добавить уникальность для соединения таблицы

add_index :users_roles, [ :user_id, :role_id ], :unique => true, :name => 'by_user_and_role'

см. В таблице соединений, какой лучший способ обхода Rails отсутствует составной ключ?

В вашей базе данных будет создано исключение, которое вы должны обработать.
Я не знаю, какая готовая для использования проверка рельсов для этого случая, но вы можете добавить свою собственную проверку следующим образом:

class User < ActiveRecord::Base
has_and_belongs_to_many :roles, :before_add => :validates_role

Я просто молча удалю вызов базы данных и сообщит об успешном завершении.

def validates_role(role)
  raise ActiveRecord::Rollback if self.roles.include? role
end

ActiveRecord:: Rollback внутренне захвачен, но не ререйзирован.

Изменить

Не используйте ту часть, где я добавляю пользовательскую проверку. Это работает, но есть лучшие альтернативы.

Используйте опцию :uniq для ассоциации в качестве @Spyros, предложенную в другом ответе:

class Parts < ActiveRecord::Base
  has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true
end  

(этот фрагмент кода из Rails Guides v.3). Читайте на Rails Guides v 3.2.13 ищите 4.4.2.19: uniq

Руководство Rails v.4 специально предупреждает об использовании include? для проверки уникальности из-за возможных условий гонки.

Часть добавления индекса для соединения с таблицей остается.

Ответ 2

Я думаю, что использование: uniq = > true гарантирует, что вы не получите дубликатов объектов. Но если вы хотите проверить, существует ли дубликат, прежде чем записывать второй в ваш db, я бы, вероятно, использовал find_or_create_by_name_and_description (...).

(Конечно, имя и описание - ваши значения столбца)

Ответ 3

Я предпочитаю

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles, -> { uniq }
end

другие опции ссылки здесь

Ответ 4

В Rails 5 вы захотите использовать distinct вместо uniq

Также попробуйте сделать это для обеспечения уникальности

has_and_belongs_to_many :foos, -> { distinct } do
  def << (value)
    super value rescue ActiveRecord::RecordNotUnique
  end
end