Railsのuniquenessのバリデーションチェックでfloatのカラムで実施しようとすると既存の値の存在チェックをwhere句のイコールで行ってしまい、意図したチェックを実行できませんでした。回避策としてカスタムバリデーターを定義して一意性のチェックを行いました。
1)一つのカラムの場合の実施例
●意図した動作にならない例
(定義内容)
validates :lat, uniqueness: true
(実行されるSQL)
Marker Exists (0.3ms) SELECT 1 AS one FROM `markers` WHERE `markers`.`lat` = BINARY 35.6878 LIMIT 1
●カスタムバリデーターを定義
・チェック対象のlatカラムは小数点以下4桁の値で入力されているので、±0.00001の範囲にある値は同一とみなすチェックを定義しました。
class MyValidator < ActiveModel::Validator def validate(record) if Marker.where("lat > ? AND lat < ?",record.lat - 0.00001,record.lat + 0.00001).exists? record.errors[:lat] << "latの値#{record.lat}はすでに存在しています。" end end end class Marker < ActiveRecord::Base include ActiveModel::Validations validates_with MyValidator end
2)二つのカラムの場合の実施例
●カスタムバリデーターを定義
latとlngは緯度と経度を示すデータでこの組み合わせが同じデータは同じ地図上の位置を表すので重複を許可しないように設定している例です。
class MyValidator < ActiveModel::Validator def validate(record) if Marker.where("lat > ? AND lat < ? AND lng > ? AND lng < ?",record.lat - 0.00001,record.lat + 0.00001,record.lng - 0.00001,record.lng + 0.00001).exists? record.errors[:lat] << "(lat,lng)の値(#{record.lat},#{record.lng})はすでに存在しています。" record.errors[:lng] << "(lat,lng)の値(#{record.lat},#{record.lng})はすでに存在しています。" end end end class Marker < ActiveRecord::Base include ActiveModel::Validations validates_with MyValidator end