Validates uniqueness of multiple columns
We came across a case where we wanted to validate that the combination of first and last names for a new person was unique. Add the following snippet
#config/initializers/validators.rb
ActiveRecord::Base.class_eval do
def self.validates_uniqueness_of_combined(*attr_names)
options = attr_names.extract_options!.symbolize_keys
attr_names = attr_names.flatten
send(validation_method(options[:on] || :save), options) do |record|
sql = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
values = attr_names.map{ |a| record.send(a) }.map{ |v| v && v.upcase }
conditions = [sql, *values]
db_record = record.class.find(:first, :conditions => conditions)
if db_record && db_record != record
default_message = "#{attr_names.map{ |attr_name| record.send attr_name }.join(' ')} has already been added"
record.errors.add_to_base(options["message"] || default_message)
end
end
end
end
to your initializers then you can do
class Person < ActiveRecord::Base validates_uniqueness_of_combined :first_name, :last_name end
Note that this does a case insensitive match on the column names. If you want case sensitive you should replace
sql = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
values = attr_names.map{ |a| record.send(a) }.map{ |v| v && v.upcase }
with
sql = attr_names.map{ |attr_name| "#{attr_name} = ?"}.join(" AND ")
values = attr_names.map{ |a| record.send(a) }
