Method: Sequel::Plugins::ValidationHelpers::InstanceMethods#validates_unique

Defined in:
lib/sequel/plugins/validation_helpers.rb

#validates_unique(*atts) ⇒ Object

Checks that there are no duplicate values in the database for the given attributes. Pass an array of fields instead of multiple fields to specify that the combination of fields must be unique, instead of that each field should have a unique value.

This means that the code:

validates_unique([:column1, :column2])

validates the grouping of column1 and column2 while

validates_unique(:column1, :column2)

validates them separately.

You can pass a block, which is yielded the dataset in which the columns must be unique. So if you are doing a soft delete of records, in which the name must be unique, but only for active records:

validates_unique(:name){|ds| ds.where(:active)}

You should also add a unique index in the database, as this suffers from a fairly obvious race condition.

This validation does not respect the :allow_* options that the other validations accept, since it can deal with a grouping of multiple attributes.

Possible Options:

:dataset

The base dataset to use for the unique query, defaults to the model’s dataset.

:message

The message to use (default: ‘is already taken’)

:only_if_modified

Only check the uniqueness if the object is new or one of the columns has been modified, true by default.

:where

A callable object where call takes three arguments, a dataset, the current object, and an array of columns, and should return a modified dataset that is filtered to include only rows with the same values as the current object for each column in the array.

If you want to do a case insensitive uniqueness validation on a database that is case sensitive by default, you can use:

validates_unique :column, where:(lambda do |ds, obj, cols|
  ds.where(cols.map do |c|
    v = obj.public_send(c)
    v = v.downcase if v
    [Sequel.function(:lower, c), v]
  end)
end)
[View source]

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/sequel/plugins/validation_helpers.rb', line 272

def validates_unique(*atts)
  opts = default_validation_helpers_options(:unique)
  if atts.last.is_a?(Hash)
    opts = opts.merge(atts.pop)
  end
  message = validation_error_message(opts[:message])
  from_values = opts[:from] == :values
  where = opts[:where]
  atts.each do |a|
    arr = Array(a)
    next if arr.any?{|x| errors.on(x)}
    cc = changed_columns
    next if opts.fetch(:only_if_modified, true) && !new? && !arr.any?{|x| cc.include?(x)}
    ds = opts[:dataset] || model.dataset
    ds = if where
      where.call(ds, self, arr)
    else
      vals = arr.map{|x| from_values ? values[x] : get_column_value(x)}
      next if vals.any?(&:nil?)
      ds.where(arr.zip(vals))
    end
    ds = yield(ds) if defined?(yield)
    unless new?
      h = ds.joined_dataset? ? qualified_pk_hash : pk_hash
      ds = ds.exclude(h)
    end
    errors.add(a, message) unless ds.empty?
  end
end