Как вы проверяете уникальность пары идентификаторов в Ruby on Rails? - PullRequest
50 голосов
/ 29 мая 2009

Предположим, следующая миграция БД в 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
, кажется, просто делает строки уникальными по идентификатору пользователя и уникальными по идентификатору вопроса, а не уникальными по паре.

Ответы [ 6 ]

89 голосов
/ 29 мая 2009
validates_uniqueness_of :user_id, :scope => [:question_id]

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

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
25 голосов
/ 24 июня 2010

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

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

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

15 голосов
/ 16 августа 2012

С RailsGuides . validates тоже работает:

class QuestionVote < ActiveRecord::Base
  validates :user_id, :uniqueness => { :scope => :question_id }
end
14 голосов
/ 06 февраля 2015

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

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

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

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

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

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

  rescue ActiveRecord::RecordNotUnique

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

10 голосов
/ 29 мая 2009

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

validates_uniqueness_of :user_id, :scope => "question_id"

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

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

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

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

  1. Напишите свой собственный метод проверки.
  2. Создать ограничение в базе данных потому что есть еще шанс, что Ваше приложение обработает две записи в в то же время.
2 голосов
/ 15 июля 2016

Когда вы создаете новую запись, это не работает, потому что 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.

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

...