Better validates_associated method for Rails 3?

后端 未结 4 739
猫巷女王i
猫巷女王i 2021-02-04 03:47

Rails 3 includes the validates_associated which is automatically called when saving a nested model. The problem with the method is the message is terrible - \"Mode

相关标签:
4条回答
  • 2021-02-04 04:24

    On the relationship, you can use :autosave => true instead which will try to save children models when you save the parent. This will automatically run the validations of the children and they will report with proper error messages.

    Moreover, if you add a presence validation on the child that the parent must be set, and you construct the child objects through the association, you don't even need the autosave flag, and you get a beautiful error message. For example:

    class Trip < ActiveRecord::Base
      validates :name, :presence => true
    
      attr_accessible :name
    
      has_many :places, dependent: :destroy, :inverse_of => :trip
    end
    
    class Place < ActiveRecord::Base
      belongs_to :trip
    
      validates :name, :trip, presence: true
    
      attr_accessible :name
    end
    

    Then you can get an nice error message with the following usage scenario:

    > trip = Trip.new(name: "California")
    => #<Trip id: nil, name: "California"> 
    > trip.places.build
    => #<Place id: nil, name: nil, trip_id: nil>
    > trip.valid?
    => false
    > trip.errors
    => #<ActiveModel::Errors:0x00000004d36518 @base=#<Trip id: nil, name: "California">, @messages={:places=>["is invalid"]}>
    > trip.errors[:places]
    => ["is invalid"] 
    

    I think validates_associated is a relic of the era before autosaving of children and isn't the best way to do things any more. Of course that's not necessarily documented well. I'm not 100% sure that this also applies to Rails 2.3, but I have a feeling it does. These changes came when the nested attributes feature was added (which was sometime in 2.x).

    This is a simplified snippet of code from a training project I posted on github.

    0 讨论(0)
  • 2021-02-04 04:29

    Most of the time validates_existence_of is all I need.

    0 讨论(0)
  • 2021-02-04 04:32

    I was having this problem, and in the end I used the solution given here by Ben Lee:

    validates associated with model's error message

    Ben says:

    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)
            return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
            record.errors.add(attribute, :invalid, options.merge(:value => value))
          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:

    module ActiveRecord
      module Validations
        class AssociatedBubblingValidator < ActiveModel::EachValidator
          def validate_each(record, attribute, value)
            (value.is_a?(Array) ? 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
    

    You can put this code in an initializer, something like /initializers/associated_bubbling_validator.rb.

    Finally, you'd validate like so:

    class User < ActiveRecord::Base
     validates_associated_bubbling :account
    end
    

    NOTE: the above code is completely untested, but if it doesn't work outright, it is hopefully enough to put you on the right track

    0 讨论(0)
  • 2021-02-04 04:35

    validates_associated runs the validations specified in the associated object's class. Errors at the parent class level simply say 'my child is invalid'. If you want the details, expose the errors on the child object (at the level of the child's form in the view).

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