Как вы проверяете уникальность пары идентификаторов в 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
.
Примечание. Не забудьте добавить уникальный столбцы базы данных.