0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】登録・更新前に重複をチェックする

Posted at

はじめに

DBの保存前に重複をチェックする実装を行った記録です。

背景

ApplicationRecordのバリデータで uniqueness: true を設定した場合

class Account < ApplicationRecord
  validates :email, uniqueness: true
end
  • オブジェクトが保存される直前に属性の値が一意であり重複していないことをチェックする
  • その属性と同じ値を持つ既存のレコードがモデルのテーブルにあるかどうかを調べるSQLクエリを実行することでバリデーションを行う
  • つまり、同時に複数の値を保存する場合などはバリデーションが行われない

実装例

  • 多対多の関連付けがされているcourseテーブルとuserテーブル、その中間テーブルであるcourse_usersテーブルがあると想定する
class Course < ApplicationRecord
  has_many :course_users
  accepts_nested_attributes_for :course_users

  has_many :users, through: course_users
end
class CourseUser < ApplicationRecord
  belongs_to :course
  belongs_to :user
end

① カスタムバリデータで実装する

class Course < ApplicationRecord
+  validates_with Validators::UniqueUserValidator
end
+ class UniqueUserValidator < ActiveModel::Validator
 
+  def validate(record)
+    user_ids = []      
+    record.course_users.each do |c|
+      if user_ids.include?(c.user_id)
+        record.errors.add(:base, '重複しています')
+        return
+      end
 
+     user_ids << c.user_id
+   end
+  end

+ end

② コールバックでバリデーションを実行する

class Course < ApplicationRecord
+  before_validation :ensure_user_is_unique


+  def ensure_user_is_unique
+    ensure_resouce_is_unique(course_users, user_id)
+  end


+  def ensure_resource_is_unique(collection, attribute_name)
+    ret = true
+    uniq = []
+    collection.each{|m|
+      if uniq.include?(m[attribute_name])
+        errors.add(:base, '重複しています')
+        ret = false
+      else
+        uniq << m[attribute_name]
+      end
+    end
+    ret
+ end
end

おわりに

Railsガイドでは下記の通りデータベースに一意性の制約の指定をするように説明しています。一意性制約を指定する際にはApplicationRecordのバリデータのみでは不完全であるということに注意したいと思います。

このバリデーションはデータベースに一意性制約(uniqueness constraint)を作成しないので、異なる2つのデータベース接続が使われていると、一意であるべきカラムに同じ値を持つレコードが2つ作成される可能性があります。これを避けるには、データベース側でそのカラムにuniqueインデックスを作成する必要があります。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?