Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Railsの技: 特定スコープ内でuniquenessバリデーションをかける(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Railsの技: 特定スコープ内でuniquenessバリデーションをかける(翻訳)

データベースでのバリデーションとアプリケーションでのバリデーションを一致させるのはよい考えです。モデルにvalidates :name, presence: trueというバリデーションがあるなら、データベース側にもそれに対応するnot null制約を付けるべきです。uniquenessバリデーションの場合は、データベースのUNIQUEインデックスと合わせて使うべきです。

現実のアプリケーションではバリデーションがもっと複雑になりがちですが、それでもできる限りこの方法を続けるべきです。

私の場合、レコードを特定のスコープ内に限って一意にする必要が生じることがよくあります。

たとえば典型的なプロジェクト管理ツールを構築中だとしましょう。Projectに持たせる名前は一意にしてUI画面内で区別できるようにしたいのですが、その名前をグローバルに一意にしたくありません。あるプロジェクトの名前をOnboadingにしたとしても、他の顧客がその名前を使うことに制約を加えるべきではありません。

ありがたいことに、Railsでは「バリデーションスコープ」という便利な機能が使えます。

使い方

Railsのuniquenessバリデーションルールでscope:オプションを使うと、uniquenessチェックで考慮すべきカラムを追加で指定できます。

class Project < ApplicationRecord
  belongs_to :account

  has_many :tasks

  validates :name, presence: true, uniqueness: { scope: :account_id }
end

このルールは「プロジェクト名は、このアカウントのスコープ内で一意でなければならない」という意味です。言い換えると、nameaccount_idの組み合わせが一意である必要がありますが、アカウントが異なれば同じプロジェクト名を使えます。

前述したように、アプリケーションレベルのバリデーションをデータベース制約にも反映したくなるでしょう。

その場合はマルチカラムインデックスを使うことになります。マルチカラムインデックスは以下のように通常のRailsマイグレーションで設定できます。

class CreateProject < ActiveRecord::Migration[6.0]
  def change
    create_table :projects do |t|
      ...
    end

    add_index :projects, [:name, :account_id], unique: true
  end
end

オプション

scope:には複数のカラムを渡せます。

たとえばレストラン向けアプリを構築していて、ゲストが1軒のレストランにつき1日に1回しか予約できないようにしたいとします。

class Reservation < ApplicationRecord
  belongs_to :guest
  belongs_to :restaurant

  validates :guest_id, uniqueness: {
    scope: [ :restaurant_id, :reservation_date ]
  }
end

デフォルトのエラーメッセージ「{field} has already been taken」のままではそっけないので、以下のようにエラーメッセージも変更するとよいでしょう。

validates :guest_id, uniqueness: {
  scope: [ :restaurant_id, :reservation_date ],
  message: "Only one reservation per guest per day is permitted"
}

原注: PostgreSQLの場合はデフォルトのインデックス名の上限が63文字までとなっています。モデルやカラム名が長くなる場合はインデックス名を変更する必要が生じるかもしれません。

add_index :reservations, [:guest_id, :restaurant_id, :reservation_date],
  unique: true,
  name: "idx_reserveration_guest_date_uniq"

参考資料

関連記事

https://techracho.bpsinc.jp/hachi8833/2021_07_15/108763


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。