web-dev-qa-db-ja.com

複数の列の一意性を検証する

実際のレコードが列だけでなく一意であることを検証するRailsの方法はありますか?たとえば、フレンドシップモデル/テーブルには、次のような複数の同一のレコードを含めることはできません。

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20
186
re5et

次のようにvalidates_uniqueness_of呼び出しをスコープできます。

validates_uniqueness_of :user_id, :scope => :friend_id
316
Dylan Markow

validates を使用して、1つの列でuniquenessを検証できます。

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

複数列の検証の構文は似ていますが、代わりにフィールドの配列を提供する必要があります。

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

ただし、、上記の検証アプローチには競合状態があり、一貫性を保証できません。次の例を考えてみましょう。

  1. データベーステーブルレコードは、nフィールドによって一意であると想定されています。

  2. 複数(2つ以上)の同時要求、個別のプロセスによって処理されます各(アプリケーションサーバー、バックグラウンドワーカーサーバー、または使用しているもの) )、データベースにアクセスして同じレコードをテーブルに挿入します。

  3. 同じnフィールドを持つレコードが存在する場合、並行して各プロセスが検証します。

  4. 各リクエストの検証は正常に渡され、各プロセスは同じデータでテーブルにレコードを作成します。

このような動作を回避するには、一意の制約をdbテーブルに追加する必要があります。次の移行を実行することで、1つ(または複数の)フィールドの add_index ヘルパーで設定できます。

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

Caveat:一意の制約を設定した後でも、2つ以上の同時要求が同じデータをdbに書き込もうとしますが、重複レコードを作成する代わりに、 ActiveRecord::RecordNotUnique 例外。個別に処理する必要があります。

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

検証には競合状態が発生するため、おそらくデータベースに実際の制約が必要になるでしょう。

validates_uniqueness_of :user_id, :scope => :friend_id

ユーザーインスタンスを永続化すると、RailsはSELECTクエリを実行してモデルを検証し、指定されたuser_idを持つユーザーレコードが既に存在するかどうかを確認します。レコードが有効であると判明した場合、RailsはINSERTステートメントを実行してユーザーを永続化します。これは、単一のプロセス/スレッドWebサーバーの単一のインスタンスを実行している場合に効果的です。

2つのプロセス/スレッドが同じ時間に同じuser_idを持つユーザーを作成しようとすると、次の状況が発生する可能性があります。 Race condition with validates

DBに一意のインデックスを配置すると、上記の状況は次のようになります。 Unique indexes on db

このブログ投稿からの回答- http://robots.thoughtbot.com/the-perils-of-uniqueness-validations

34
Deepak Azad

これは、2つの列のデータベース制約を使用して実行できます。

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

Railsバリデーターを使用できますが、一般的にはデータベース制約の使用をお勧めします。

もっと読む: https://robots.thoughtbot.com/validation-database-constraint-or-both

2
Tate Thurston