ActiveRecord4のバリデーションで複数カラムにユニーク制約を付ける方法

ActiveRecord4のバリデーションで複数カラムにユニーク制約を付ける方法メモ。



ActiveRecord4のバリデーションで複数カラムにユニーク制約を付ける方法


usersテーブルにuser_id、item_idカラムがある時、この2つのカラムの組み合わせでユニーク制約のバリデーションを行うコードは下記の通り。


# app/model/user.rb
class User < ActiveRecord::Base
  validates :user_id,
    uniqueness: {
      message: "、item_idが同じ組み合わせのレコードが既に存在します。",
      scope: [:item_id]
    }
end

scopeの配列に値を追加すれば、3カラム以上のユニーク制約バリデーションにすることも出来ます。


上記のバリデーションではuser_idカラムにのみエラー情報がくっつくため、HTMLフォームと組み合わせて使っている場合はuser_idのフォームにのみエラー情報が表示される点に注意が必要です。


Railsのモデルに複合一意制約を定義する方法」にもう少し詳しく書いてあります。


また、ユニーク制約のバリデーションを行う場合でも、テーブルにもユニーク制約を付けるのを忘れないようにしましょう。バリデーションだけだとレースコンディションの場合に問題が発生します。


class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.integer :user_id, :null => false
      t.integer :item_id, :null => false
      t.timestamps

      t.index [:user_id, :item_id], :unique => true
    end
  end
end

※テーブル定義は説明用です。ぱっと見おかしい設計なので真似しないようにしましょう(^^)



テーブル定義のユニーク制約とrailsのユニーク制約バリデーションはどう使い分ければいいの?


このよくある質問にお答えすると、


railsのバリデーションに引っかかるのはユーザーの入力ミスであり、アプリケーション側でリカバリー手段を用意すべき。テーブル定義のユニーク制約に引っかかって例外が発生する(つまりバリデーション漏れがある)のはアプリケーションのバグであり、例外が起きないようにすべき。


という風にすればいいんじゃないかと思います(^^)



参考リンク


Railsのモデルに複合一意制約を定義する方法


著者プロフィール
Webサイトをいくつか作っています。
著者プロフィール