HABTM - ограничение уникальности - PullRequest
17 голосов
/ 14 февраля 2011

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

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

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

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

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

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

Ответы [ 4 ]

37 голосов
/ 14 февраля 2011

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

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 :: Откат захвачен изнутри, но не восстановлен.

Редактировать

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

Используйте параметр :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 Guide v.4 специально предупреждает против использования include? для проверки уникальности из-за возможных условий гонки.

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

5 голосов
/ 22 сентября 2016

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

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

has_and_belongs_to_many :foos, -> { distinct } do
  def << (value)
    super value rescue ActiveRecord::RecordNotUnique
  end
end
5 голосов
/ 26 мая 2014

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

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

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

5 голосов
/ 14 февраля 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...