Tuesday 11 March 2014

How to get rid of "X is invalid" error message in validates_associated method in rails

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

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

class CheckInvoice < ActiveRecord::Base
  belongs_to :check

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

    module ClassMethods
      def own_validates_associated(*attr_names)
        validates_with MyCustomValidatesAssociated, _merge_attributes(attr_names)

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

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) }
    final_errors.each { |err| self.errors.add(*err) }
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!!!.