Как вы проверяете уникальность пары идентификаторов в Ruby on Rails?

Предположим, что следующая миграция DB в Ruby:

    create_table :question_votes do |t|
      t.integer :user_id
      t.integer :question_id
      t.integer :vote

      t.timestamps
    end

Предположим далее, что я хочу, чтобы строки в БД содержали уникальные пары (user_id, question_id). Какая правая пыль, чтобы положить в модель, чтобы выполнить это?

validates_uniqueness_of :user_id, :question_id
, кажется, просто делает строки уникальными по идентификатору пользователя и уникален идентификатором вопроса, а не уникальным по паре.

Ответ 1

validates_uniqueness_of :user_id, :scope => [:question_id]

если вам нужно включить другой столбец (или более), вы можете добавить его также в область. Пример:

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]

Ответ 2

Если вы используете mysql, вы можете сделать это в базе данных с помощью уникального индекса. Это что-то вроде:

add_index :question_votes, [:question_id, :user_id], :unique => true

Это приведет к возникновению исключения при попытке сохранить удвоенную комбинацию question_id/user_id, поэтому вам придется поэкспериментировать и выяснить, какое исключение можно поймать и обработать.

Ответ 3

Из RailsGuides. validates тоже работает:

class QuestionVote < ActiveRecord::Base
  validates :user_id, :uniqueness => { :scope => :question_id }
end

Ответ 4

За исключением написания собственного метода проверки, лучше всего использовать validates_uniqueness_of:

validates_uniqueness_of :user_id, :scope => "question_id"

Это проверит, что user_id уникален во всех строках с тем же question_id, что и запись, которую вы пытаетесь вставить.

Но это не то, что вы хотите.

Я считаю, что вы ищете комбинацию :user_id и :question_id, чтобы быть уникальной в базе данных.

В этом случае вам нужно сделать две вещи:

  • Напишите свой собственный метод проверки.
  • Создать ограничение в базе данных потому что еще есть шанс, что ваше приложение обработает две записи в в то же время.

Ответ 5

Лучший способ - использовать оба варианта, поскольку рельсы не на 100% надежны, если проверка достоверности уникальна.

Вы можете использовать:

  validates :user_id, uniqueness: { scope: :question_id }

и быть на 100% безопасным, добавьте эту проверку на свой db (MySQL ex)

  add_index :question_votes, [:user_id, :question_id], unique: true

а затем вы можете обращаться с контроллером, используя:

  rescue ActiveRecord::RecordNotUnique

Итак, теперь вы на 100% уверены, что у вас не будет дублированного значения:)

Ответ 6

Когда вы создаете новую запись, это не работает, потому что id вашей родительской модели еще не существует в момент проверки.

Это должно сработать для вас.

class B < ActiveRecord::Base
  has_many :ab
  has_many :a, :through => :ab
end

class AB < ActiveRecord::Base
  belongs_to :b
  belongs_to :a
end

class A < ActiveRecord::Base
  has_many :ab
  has_many :b, :through => :ab

  after_validation :validate_uniqueness_b

  private
  def validate_uniqueness_b
    b_ids = ab.map(&:b_id)
    unless b_ids.uniq.length.eql? b_ids.length
      errors.add(:db, message: "no repeat b's")
    end
  end
end

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

Примечание. Не забудьте добавить уникальный столбцы базы данных.