While i am using validates_associated in my project, it gave "x(some model) name is invalid" error. I will give proper example. Batch is one model, Check is another model and Check invoices is another model. So my active record models looks like as follows.
class Batch < ActiveRecord::Base
has_many :checks, dependent: :destroy
has_many :check_invoices, through: :checks
accepts_nested_attributes_for :checks, allow_destroy: true
validates_associated :checks
end
class Check < ActiveRecord::Base
# Associations
belongs_to :batch
belongs_to :borrower
has_many :check_invoices, dependent: :destroy
accepts_nested_attributes_for :check_invoices, allow_destroy: true
validates_associated :check_invoices
end
class CheckInvoice < ActiveRecord::Base
belongs_to :check
end
so my case, while i am trying to save Batch(parent model) user can enter check details as well as check invoices details also.
Problem is, any check model is invalid its giving error messages associated with checks but it gave some unwanted message like "Checks is invalid". Because validates_associated default message option has "is invalid" string. So this default message will append in your child associated model even though if you define individual level error messages.
Solution 1: Above i explained what is the problem, now i am going to explain how to fix it in global manner/individual model level.
lets create one customer validator file called "my_custom_validates_associated.rb" in config/initializers folder in your rails application.
That file should have following content which you can use anywhere in the models.
module ActiveRecord
module Validations
class MyCustomValidatesAssociated< ActiveModel::EachValidator
def validate_each(record, attribute, value)
#(value.is_a?(Array) ? value : [value]).each do |v|
# unless v.nil?
# record.errors.full_messages.each do |msg|
# #record.errors.add(:base, "msg")
# end
# end
#end
end
end
module ClassMethods
def own_validates_associated(*attr_names)
validates_with MyCustomValidatesAssociated, _merge_attributes(attr_names)
end
end
end
end
After adding this file into your config/initializer folder then you have to restart your server then only you will get effect.
Now your model needs little bit change due to our new custom validator added into our application.
Now my own validation associated should work with only checks model then i have to change in Batch model. Now new code looks like as follows.
class Batch < ActiveRecord::Base
has_many :checks, dependent: :destroy
has_many :check_invoices, through: :checks
belongs_to :cash_receipt_source_code ,:foreign_key => "batch_source_code_id"
accepts_nested_attributes_for :checks, allow_destroy: true
own_validates_associated :checks
end
Now you wont get "X is invalid/Checks is invalid" default error message in your error messages list.
Solution 2:
The above one is created for generic level. If you want to use only one model then you can filter error messages and remove "X is invalid" message then assign back to other error messages into model.errors.add method. Those as follows for your reference.
def after_validation
# you can filter unwanted messages that is not userfriendly.
final_errors = self.errors.reject{ |err| %w{ user User }.include?(err.first) }
self.errors.clear
final_errors.each { |err| self.errors.add(*err) }
end
in your model, you can to call the above method on after_validation call back.
Here i attached final output before and after the code change for your understanding.
Before custom validator how the message is coming with "Checks is invalid"
After we added custom validator called own_validates_associated method,see the desired output error messages.
Hopefully you can enjoy this article. Thanks for reading!!!.
class Batch < ActiveRecord::Base
has_many :checks, dependent: :destroy
has_many :check_invoices, through: :checks
accepts_nested_attributes_for :checks, allow_destroy: true
validates_associated :checks
end
class Check < ActiveRecord::Base
# Associations
belongs_to :batch
belongs_to :borrower
has_many :check_invoices, dependent: :destroy
accepts_nested_attributes_for :check_invoices, allow_destroy: true
validates_associated :check_invoices
end
class CheckInvoice < ActiveRecord::Base
belongs_to :check
end
so my case, while i am trying to save Batch(parent model) user can enter check details as well as check invoices details also.
Problem is, any check model is invalid its giving error messages associated with checks but it gave some unwanted message like "Checks is invalid". Because validates_associated default message option has "is invalid" string. So this default message will append in your child associated model even though if you define individual level error messages.
Solution 1: Above i explained what is the problem, now i am going to explain how to fix it in global manner/individual model level.
lets create one customer validator file called "my_custom_validates_associated.rb" in config/initializers folder in your rails application.
That file should have following content which you can use anywhere in the models.
module ActiveRecord
module Validations
class MyCustomValidatesAssociated< ActiveModel::EachValidator
def validate_each(record, attribute, value)
#(value.is_a?(Array) ? value : [value]).each do |v|
# unless v.nil?
# record.errors.full_messages.each do |msg|
# #record.errors.add(:base, "msg")
# end
# end
#end
end
end
module ClassMethods
def own_validates_associated(*attr_names)
validates_with MyCustomValidatesAssociated, _merge_attributes(attr_names)
end
end
end
end
After adding this file into your config/initializer folder then you have to restart your server then only you will get effect.
Now your model needs little bit change due to our new custom validator added into our application.
Now my own validation associated should work with only checks model then i have to change in Batch model. Now new code looks like as follows.
class Batch < ActiveRecord::Base
has_many :checks, dependent: :destroy
has_many :check_invoices, through: :checks
belongs_to :cash_receipt_source_code ,:foreign_key => "batch_source_code_id"
accepts_nested_attributes_for :checks, allow_destroy: true
own_validates_associated :checks
end
Now you wont get "X is invalid/Checks is invalid" default error message in your error messages list.
Solution 2:
The above one is created for generic level. If you want to use only one model then you can filter error messages and remove "X is invalid" message then assign back to other error messages into model.errors.add method. Those as follows for your reference.
def after_validation
# you can filter unwanted messages that is not userfriendly.
final_errors = self.errors.reject{ |err| %w{ user User }.include?(err.first) }
self.errors.clear
final_errors.each { |err| self.errors.add(*err) }
end
in your model, you can to call the above method on after_validation call back.
Here i attached final output before and after the code change for your understanding.
Before custom validator how the message is coming with "Checks is invalid"
Hopefully you can enjoy this article. Thanks for reading!!!.