Experiences on the Front Lines of User Interfaces and Web Development

How to override validation of has_many active record objects

I found myself needing to override the validation of "has_many" active record objects after using build to create them. The reason being that I was doing custom validation on each of the built objects anyway and wanted to display each of the child object's validation error message, not the generic "<object> is not valid".

For example:

class List < ActiveRecord::Base

has_many :phone_numbers, :dependent => :destroy

validate :validate_numbers

def build_items_from_file_stream( stream )
stream.split( "\n" ).each_with_index do |line, index|
next if line.blank?
phone = self.phone_numbers.build( :phone_number => line )
phone.file_line_number = index + 1
phone.unformatted_number = line
end
end

# this method overrides the automatically generated method
def validate_associated_records_for_phone_numbers
true
end

def validate_numbers
self.phone_numbers.each do |phone|
if !phone.valid?
error_prefix = "File line #{phone.file_line_number}, '#{phone.unformatted_number}': "
errors.add error_prefix, target.errors.full_messages.join( ", " )
end
end
end

end


Basically ActiveRecord will automatically create a validate method of the name:
validate_associated_records_for_
That method steps across all of the associated objects, and if !valid? does:
errors.add , "is not valid"

Without overriding that method, if you were to create a list (in my example above) that had an invalid phone number (assuming the phone_numbers class has validation too), you would get an error string something like:
- Phone number is not valid
- File line 1 '805-555-123': national number must contain 10 digits
- Phone number is not valid
- File line 2 '+11 805-555-1234': country code not found

Clearly having "phone number is invalid" multiple times is not adding any value, and in fact making the list validation message more confusing.
comments powered by Disqus