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


193

実際のレコードが列だけでなく一意であることを検証するための鉄道の方法はありますか?たとえば、友情モデル/テーブルは、次のような複数の同一のレコードを持つことはできません。

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

7
私が密集している場合は私を許してください、しかしそれはこの状況でどのように役立ちますか?
2011

2
モデルで「validates_uniqueness_of」を使用してみてください。これが機能しない場合は、add_index:table、[:column_a、:column_b]、:unique => true)のようなステートメントを含むフィールドの移行を作成できるインデックスを作成しようとします
Harry Joy

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制約の方法を説明する回答に賛成投票します。
Pooyan Khosravi 2014年

1
より良い答えは、以下に記載されているすべてがこの1つのstackoverflow.com/a/34425284/1612469です。これにより、すべてが正しく機能することを確認するための別のレイヤーがもたらされます
Aleks

回答:


319

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

validates_uniqueness_of :user_id, :scope => :friend_id

83
3つ以上のフィールドで一意性を検証する必要がある場合に備えて、複数のスコープパラメータを渡すことができることを追加したかっただけです。すなわち:scope => [:friend_id、:group_id]
Dave Rapin

27
あなたが言うことができないのは奇妙ですvalidates_uniqueness_of [:user_id, :friend_id]。多分これはパッチが必要ですか?
Alexey

12
Alexey、validates_uniqueness_of [:user_id、:friend_id]は、リストされた各フィールドの検証のみを行います-そして、それは文書化され、予想される動作です
Nikita Hismatov '18

71
Rails 4では、次のようになります。validates:user_id、uniqueness:{scope::friend_id}
マリーナマーティン

3
おそらく:message => 'はこの友達を持っています。
laffuste 2014年

137

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

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

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

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

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

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

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

  3. 各プロセスは並行して、同じnフィールドのレコードがあるかどうかを検証します。

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

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

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

警告:一意の制約を設定した後でも、2つ以上の同時リクエストが同じデータをデータベースに書き込もうとしますが、重複したレコードを作成する代わりに、ActiveRecord::RecordNotUnique例外が発生します。これは個別に処理する必要があります。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.