バリデーション uniqueness

ブックマーク機能を作る際、unique制約が必要だったので理解を深めるためにまとめます!

なぜunique制約が必要なのか

ブックマーク機能は、どのユーザーが、どの掲示板をブックマークしたという関係性を保存することです。
解除する際は保存したデータを消す必要があり、データが重複していると消したはずなのに同じデータが残ってしまうことがあり、一意性が失われてしまいます。

uniquenessとは

このヘルパーは、オブジェクトが保存される直前に、属性の値が一意(unique)であり重複していないことを検証する。

class Bookmark < ApplicationRecord
  validates :user_id, uniqueness: true
end

上記のコードだと、1ユーザーに対して1つしかブックマークできないようになっています。このままでは作りたい要件とは異なってしまいます。
そこでscopeを使用します。
scopeとはuniquenessに用意されているオプションで、範囲を指定して、一意かどうかをチェックしてくれます。

class Bookmark < ApplicationRecord
 validates :user_id, uniqueness: { scope: :board_id } 
end

上記のコードは、1ユーザーが1掲示板に1ブックマークという範囲を指定しています。

:scopeを用いる一意性バリデーションの違反を防止する目的でデータベース側に制約を作成したい場合は、データベース側で両方のカラムにuniqueインデックスを作成しなければなりません。

class CreateBookmarks < ActiveRecord::Migration[5.2]
  def change
    create_table :bookmarks do |t|
      t.references :user, foreign_key: true
      t.references :board, foreign_key: true

      t.timestamps
    end
    add_index  :bookmarks, [:user_id, :board_id], unique: true
  end
end