Question

I'm working on an RoR project and I'd like to have a uniqueness validation on one of my models that checks against a custom scope:

class Keyword < ActiveRecord::Base
  belongs_to :keyword_list

  scope :active, -> { where("expiration > ?", DateTime.now) }

  validates :name, uniqueness: { scope: [:active, :keyword_list_id] }
end

Only, this doesn't work. It checks the database for an active column, which doesn't exist and throws this error:

ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column keywords.active does not exist

So, my question is there any way to make this work, or do I have to write a custom validator? And if so, are there any tips on what it should look like to hitting the database too much?

Was it helpful?

Solution

No, you will have to write a custom validation.

Try this.

# In app/models/keyword.rb

validate :freshness

private

def freshness
  if Keyword.active.find_by(name: self.name, keyword_list_id: self.keyword_list_id)
    errors.add(:base, "Name is not fresh enough.") # Fails the validation with error message
  end
end

Here's another interesting point, you cannot rely on validates_uniqueness_of, or any other uniqueness validator in rails, because validations are not run atomically, meaning that if two identical records are inserted at the same time, and there is no SQL constraint validating uniqueness, the ruby validations will pass and both records will be inserted.

What I'm trying to say here is that if your validations are mission-critical, use a SQL constraint.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top