Перевірте унікальність декількох стовпців


193

Чи існує спосіб рельсової перевірки того, що фактичний запис унікальний, а не лише стовпець? Наприклад, модель / таблиця дружби не повинна мати декілька однакових записів, таких як:

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20

7
пробач, якщо я щільний, але як би це допомогло в цій ситуації?
re5et

2
спробуйте використовувати "validates_uniqueness_of" у своїй моделі. якщо це не спрацює, спробуйте створити індекс, за допомогою якого можна створити міграцію feilds, яка включає в себе такий вираз, як add_index: table, [: column_a,: column_b],: unique => true)
Гаррі Джой

1
@HarryJoy, запитав він Is there a rails-way way. І ви пропонуєте йому безрельсовий шлях, але стандартний. The Active Record way claims that intelligence belongs in your models, not in the database.
Зелений

2
На жаль validates :field_name, unique: true, схильний до умов перегонів, тому, хоч і проти рейок, фактичне обмеження є кращим. @HarryJoy Я підтримаю відповідь, що описує спосіб обмеження.
Пуян Хосраві

1
краща відповідь, тоді все зазначене нижче - це один stackoverflow.com/a/34425284/1612469, оскільки він приносить ще один шар, щоб переконатися, що все буде працювати правильно
Алекс

Відповіді:


319

Ви можете скористатися validates_uniqueness_ofвикликом наступним чином.

validates_uniqueness_of :user_id, :scope => :friend_id

83
Просто хотів би додати, що ви можете передавати декілька параметрів діапазону, якщо вам потрібно перевірити унікальність на більш ніж 2 полях. Тобто: obseg => [: friend_id,: group_id]
Дейв Рапін

27
Дивно, що ви не можете сказати validates_uniqueness_of [:user_id, :friend_id]. Може, це потрібно зафіксувати?
Олексій

12
Олексій, перевіряє_унікальність_of [: user_id,: friend_id] просто виконає перевірку для кожного з перелічених полів - і це задокументовано та очікувана поведінка
Микита Гісматов

71
У Rails 4 це стає: validates: user_id, унікальність: {obseg:: friend_id}
Марина Мартін

3
Ви, ймовірно, хочете додати користувальницьку помилку msg, наприклад: message => 'вже має цей друг.'
laffuste

137

Ви можете validatesперевірити uniquenessв одному стовпці:

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

Синтаксис для перевірки на кількох стовпцях схожий, але вам слід надати масив полів:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}

Однак підходи до валідації, показані вище, мають умову перегонів і не можуть забезпечити послідовність. Розглянемо наступний приклад:

  1. Записи таблиці баз даних повинні бути унікальними по n полів;

  2. багаторазові ( два або більше ) одночасних запитів, що обробляються окремими процесами ( сервери додатків, фонові сервери робітників або все, що ви використовуєте ), база даних, щоб вставити один і той же запис у таблицю;

  3. кожен процес паралельно підтверджує, якщо є запис з однаковими n полями;

  4. перевірка для кожного запиту проходить успішно, і кожен процес створює запис у таблиці з однаковими даними.

Щоб уникнути подібної поведінки, слід додати унікальне обмеження до таблиці db. Ви можете встановити його за допомогою add_indexпомічника для одного (або кількох) полів, виконавши таку міграцію:

class AddUniqueConstraints < ActiveRecord::Migration
  def change
   add_index :table_name, [:field1, ... , :fieldn], unique: true
  end
end

Caveat : навіть після встановлення унікального обмеження два чи більше одночасних запитів намагатимуться записати ті самі дані в db, але замість створення дублікатів записів, це призведе до ActiveRecord::RecordNotUniqueвинятку, з яким слід обробляти окремо:

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 

2

Це можна зробити за допомогою обмеження бази даних у двох стовпцях:

add_index :friendships, [:user_id, :friend_id], unique: true

Ви можете використати валідатор рейкових шин, але загалом рекомендую використовувати обмеження для бази даних.

Більше читання: https://robots.thoughtbot.com/validation-database-constraint-or-both

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.