LoginSignup
54
37

More than 3 years have passed since last update.

uniqueness: scope を使ったユニーク制約方法の解説

Posted at

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インデックスを作成します.

label.rb
class AddUniqueIndexToLabels < ActiveRecord::Migration
  def change
    add_index :labels, [:name, :user_id], unique: true
  end
end

おわりに

ActiveRecordの一意性検証の範囲指定について学びました.

54
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
37