web-dev-qa-db-ja.com

Ruby on Rails)でIDのペアの一意性をどのように検証しますか?

Rubyで次のDB移行を想定します。

 create_table:question_votes do | t | 
 t.integer:user_id 
 t.integer:question_id 
 t.integer:vote 
 
 t。タイムスタンプ
終了

さらに、DBの行に一意の(user_id、question_id)ペアが含まれていると仮定します。それを達成するためにモデルに入れる適切なダストは何ですか?

validates_uniqueness_of:user_id、:question_id
50
dfrankow
validates_uniqueness_of :user_id, :scope => [:question_id]

別の列(またはそれ以上)を含める必要がある場合は、それをスコープに追加することもできます。例:

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
89
Demi

Mysqlを使用している場合は、一意のインデックスを使用してデータベースで実行できます。それは次のようなものです:

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

これにより、question_id/user_idの2倍にされた組み合わせを保存しようとすると例外が発生するため、どの例外をキャッチして処理するかを実験して把握する必要があります。

25
pakeha

RailsGuides から。 validatesも機能します:

class QuestionVote < ActiveRecord::Base
  validates :user_id, :uniqueness => { :scope => :question_id }
end
16
Jonathan Lin

Railsは一意性の検証が行われると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%安全になり、重複した値がなくなります:)

14
dpedoneze

独自の検証メソッドを作成する場合を除いて、validates_uniqueness_ofで実行できる最善の方法は次のとおりです。

validates_uniqueness_of :user_id, :scope => "question_id"

これにより、挿入しようとしているレコードと同じquestion_idを持つすべての行内でuser_idが一意であることを確認します。

しかし、それはあなたが望むものではありません

:user_id:question_idの組み合わせがデータベース全体で一意になるように探していると思います。

その場合、次の2つのことを行う必要があります。

  1. 独自の検証メソッドを記述します。
  2. アプリが2つのレコードを同時に処理する可能性があるため、データベースに制約を作成します。
10
eggdrop

新しいレコードを作成するときは、検証の時点では親モデルの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

注:データベースの列に一意を追加することを忘れないでください

2
Francisco Balam