Ruby on Rails ActiveRecord "has_many: через" проверку уникальности

В настоящее время я вставляю новое отношение, проверяя каждый раз, если оно не существует:

unless Relationship.exists?(:entry_id => entry.id, :tag_id => tag.id)

Как я могу реализовать такую ​​проверку внутри модели отношений, чтобы она не позволяла иметь более одной связи между одной и той же записью и тегом?

Ответ 1

class Relationship < ActiveRecord::Base
  belongs_to :entry
  belongs_to :tag
  validates :tag_id, :uniqueness => { :scope => :entry_id }
end

Ответ 2

Предполагая, что ваши модели выглядят примерно так:

class Entry < ActiveRecord::Base
  has_many :relationships
  has_many :tags, :through => :relationships
end

class Tag < ActiveRecord::Base
  has_many :relationships
  has_many :entries, :through => :relationships
end

class Relationship < ActiveRecord::Base
  belongs_to :entry
  belongs_to :tag
end

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

validates_uniqueness_of :tag_id, :scope => :entry_id

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

SELECT `relationships`.id
FROM `relationships`
WHERE (`relationships`.`tag_id` = <tag id> AND `relationships`.`entry_id` = <entry id>)
LIMIT 1

(который вы заметите, по сути, тот же SQL, сгенерированный явным использованием Relationship.exists?(:entry_id => entry.id, :tag_id => tag.id)), и если запись найдена, проверка не будет выполнена.

Также, как и в любом случае, когда вы хотите проверить уникальность, убедитесь, что у вас есть уникальный ключ на tag_id, entry_id в вашей таблице relationships. См. эту статью и "Concurrency и целостность" страницы API, с которой я связан выше, для получения дополнительной информации.