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?
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.