I have two model as follows
class User < ActiveRecord::Base
validates_associated :account
end
class Account < ActiveRecord::Base
belongs_to :user
May be you can try something as given below
validates_associated :account , :message=> lambda{|class_obj, obj| obj[:value].errors.full_messages.join(",") }
Through obj[:value]
you're accessing the validated Account object. So will obj[:value].errors give you errors.
A vanilla solution would be to have a second error's rendering for the associated object:
<%= render :partial => 'shared/errors', :locals => {:instance => @account.owner} %>
<%= render :partial => 'shared/errors', :locals => {:instance => @account} %>
You can write your own custom validator, based on the code for the built-in validator.
Looking up the source code for validates_associated, we see that it uses the "AssociatedValidator". The source code for that is:
module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
record.errors.add(attribute, :invalid, options.merge(:value => value))
end
end
end
module ClassMethods
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
So you can use this as an example to create a custom validator that bubbles error messages like this (for instance, add this code to an initializer in config/initializers/associated_bubbling_validator.rb
):
module ActiveRecord
module Validations
class AssociatedBubblingValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
((value.kind_of?(Enumerable) || value.kind_of?(ActiveRecord::Relation)) ? value : [value]).each do |v|
unless v.valid?
v.errors.full_messages.each do |msg|
record.errors.add(attribute, msg, options.merge(:value => value))
end
end
end
end
end
module ClassMethods
def validates_associated_bubbling(*attr_names)
validates_with AssociatedBubblingValidator, _merge_attributes(attr_names)
end
end
end
end
So you can now validate like so:
class User < ActiveRecord::Base
validates_associated_bubbling :account
end
Also, be sure to add a validate: false
in your has_many
association, otherwise, Rails will validate the association by default and you'll end up with two error messages, one given by your new AssociatedBubblingValidator and one generic given by Rails.
If anyway needs it, the following works on Rails 5.2
#app/validators/with_own_error_messages_validator.rb
class WithOwnErrorMessagesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
Array(value).each do |v|
unless v.valid?
v.errors.full_messages.each do |msg|
record.errors.add(attribute, msg, options.merge(value: value))
end
end
end
end
end
#app/models/project.rb
class Project < ApplicationRecord
validates :attachments, with_own_error_messages: true
has_many :attachments, as: :attachable, validate: false
end
Note the validate: false
on the has_many
association. This is necessary since Rails now validates associations by default. Without it, you get the Attachment
error message(s) plus a generic Attachments is invalid
.
Hope this helps.
For those still on Rails 2, you can overwrite validates_associated with the following code:
module ActiveRecord::Validations::ClassMethods
def validates_associated(association, options = {})
class_eval do
validates_each(association) do |record, associate_name, value|
associate = record.send(associate_name)
if associate && !associate.valid?
associate.errors.each do |key, value|
record.errors.add(key, value)
end
end
end
end
end
end
Source: http://pivotallabs.com/alias-method-chain-validates-associated-informative-error-message/
To get the error messages for account, you would have to call the errors
method on that instance:
@user.account.errors
or
@account = @user.build_account
@account.errors
or in the view:
<%= error_messages_for :account %>
I'm making the assumption that it's a has_one relationship.