#uniqueness: scope を使ったユニーク制約方法の解説
uniqueness: scopeを利用して一意性検証をする方法について解説します。
目次
動作環境
OS : macOS Mojave 10.14.6
ruby : 2.6.3p62
rails : 5.2.4
##実装例
class Label < ApplicationRecord
has_many :labelings, dependent: :destroy
belongs_to :user, optional: true
validates :name, presence: true, uniqueness: { scope: :user }
end
ユーザはタスクに紐付けるラベルを作成することができますが、各ユーザは同じ名前のラベルを作れないようにラベルモデルのnameカラムに一意性制約をつけています。
実行結果[5]を見るとRollbackしています.
[3] pry(main)> user = User.first
[4] pry(main)> user.labels.create(name:'test-label')
(0.2ms) BEGIN
(1.6ms) COMMIT
#同じユーザで同名のラベルを作成する
[5] pry(main)> user.labels.create(name:'test-label')
#Rollbackする
(0.3ms)ROLLBACK
#違うユーザで検証
[6] pry(main)> user2 = User.last
[7] pry(main)> user2.labels.create(name:'test-label')
(5.7ms) BEGIN
#書き込み成功
(34.0ms) COMMIT
解説
scope
を付けない場合,テーブル全体で一つの名前のラベル名しか保存できません。
validates :name, uniqueness:true
つまりscopeという文字通りscopeの中での一意性制約にするオプションです。
このことによって各ユーザごとに一意となるカテゴリを作成することができます。
複数のscope
またscopeは配列により複数作成することもできます。
scope は配列にして複数指定できます。
validates :name, uniqueness: { scope: [:group_id, :user_id] }
これでname, group_id, user_id の全てが同じデータは1件しか作成できないように制約できます.
データベース側の制約
また上記だけでなくデータベース側にも制約を作成する場合は、以下のように両方のカラムにuniqueインデックスを作成します.
class AddUniqueIndexToLabels < ActiveRecord::Migration
def change
add_index :labels, [:name, :user_id], unique: true
end
end
おわりに
ActiveRecordの一意性検証の範囲指定について学びました.