Adding dirty checking by default to uniqueness validations

I find it a bit odd that validates_uniqueness_of always hits the database even when the attribute we are concerned with has not changed. It’s easy enough to add a dirty checking condition to a uniqueness validation, but it seems like this should be the default behavior and always running the validation should require some sort of a flag. Would this change be accepted?



Hey Ryan,

No idea if this would be accepted, but I have two questions:

  1. Do any other validators have this behavior? I don’t think so, but I’m not sure. Consistency isn’t always the highest priority, but it’s an important consideration. I guess you could argue that this validator is special because it requires database querying. I also wonder how this would work when adding validators to existing models with extant DB records, i.e., if I load a model with newly-added validators and call valid?, I want all of the validators to be run regardless of whether I changed any attributes.

  2. Is truly easy to add the dirty check? In simple cases, yes, but that validator has some fairly sophisticated options that would complicate a dirty check, I think. From the API docks:

validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]

validates_uniqueness_of :title, conditions: -> { where.not(status: ‘archived’) }

Those two examples would at least require checking other attributes. It seems like that would be difficult in some cases and impossible in the case of raw SQL conditions. I guess you could always punt on more complicated uses of the validator, but that would again lead back to inconsistency and unpredictability about when the validation performs the dirty check.


You can get the behavior you want with an `if` option, I believe:

validates_uniqueness_of :foo, if: :foo_changed?

The behavior of all the *other* validations is “run every time the record is saved”, so I don’t think it would make sense for one of them to be different out-of-the-box.

—Matt Jones