validates associated with model's error message

前端 未结 7 1497
孤独总比滥情好
孤独总比滥情好 2020-12-08 10:04

I have two model as follows

class User < ActiveRecord::Base
 validates_associated :account
end

class Account < ActiveRecord::Base

  belongs_to :user
         


        
相关标签:
7条回答
  • 2020-12-08 10:37

    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.

    0 讨论(0)
  • 2020-12-08 10:39

    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} %>
    
    0 讨论(0)
  • 2020-12-08 10:43

    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.

    0 讨论(0)
  • 2020-12-08 10:44

    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.

    0 讨论(0)
  • 2020-12-08 10:45

    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/

    0 讨论(0)
  • 2020-12-08 10:55

    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.

    0 讨论(0)
提交回复
热议问题