Sometimes you have a dynamic form that uses accepts_nested_attributes_for with uniqueness validations in the nested models. This can cause problems for uniqueness validation.
For example: You have unique numbers for each. You have an existing 1, 2, 3. You delete/mark number 3 for destruction. You then add a new one which now has the number three. If you just use default validation for this it will complain about the new one saying that number has already been taken. This is because the validations are done before the deletion.
You need to validate them in memory. Heres how:
class Parent < ActiveRecord::Base
has_many :children
accept_nested_attributes_for :children, reject_if: :all_blank, allow_destroy: true
validate :validate_unique_connection_numbers
def validate_unique_connection_numbers
#the second argument is an array so it can take multiple fields
validate_uniqueness_of_in_memory(children, [:number], 'Child already exists with that number')
end
end
### config/initializers/validates_uniqueness_in_memory.rb - could probably be in app/validations/ instead
module ActiveRecord
class Base
def validate_uniqueness_of_in_memory(collection, attrs, message)
hashes = collection.inject({}) do |hash, record|
key = attrs.map {|a| record.send(a).to_s }.join
if key.blank? || record.marked_for_destruction?
key = record.object_id
end
hash[key] = record unless hash[key]
hash
end
if collection.length > hashes.length
self.errors.add(:base, message)
end
end
end
end
Now it will be verified in memory taking into account the ones marked for deletion.
**Note: This is written for Rails 3.2. I heard somewhere this was changed in Rails 4 but I dont know if that is true.
**Credits: Andrew France originally wrote this for Rails 2. Thanks bud!
Related External Links: