Ruby on Rails: how to get error messages from a child resource displayed?

后端 未结 8 1297
谎友^
谎友^ 2020-12-12 16:58

I\'m having a difficult time understanding how to get Rails to show an explicit error message for a child resource that is failing validation when I render an XML template.

相关标签:
8条回答
  • 2020-12-12 17:36

    Add a validation block in the School model to merge the errors:

    class School < ActiveRecord::Base
      has_many :students
    
      validate do |school|
        school.students.each do |student|
          next if student.valid?
          student.errors.full_messages.each do |msg|
            # you can customize the error message here:
            errors.add_to_base("Student Error: #{msg}")
          end
        end
      end
    
    end
    

    Now @school.errors will contain the correct errors:

    format.xml  { render :xml => @school.errors, :status => :unprocessable_entity }
    

    Note:

    You don't need a separate method for adding a new student to school, use the following syntax:

    school.students.build(:email => email)
    

    Update for Rails 3.0+

    errors.add_to_base has been dropped from Rails 3.0 and above and should be replaced with:

    errors[:base] << "Student Error: #{msg}"
    
    0 讨论(0)
  • 2020-12-12 17:36

    I'm not sure if this is the best (or a correct) answer...i'm still learning, but I found this to work pretty well. I haven't tested it extensively, but it does seem to work with rails4:

    validate do |school|
      school.errors.delete(:students)
      school.students.each do |student|
        next if student.valid?
        school.errors.add(:students, student.errors)
      end
    end
    
    0 讨论(0)
  • 2020-12-12 17:37

    Here's an example that could stand some DRYing:

    def join_model_and_association_errors!(model)
      klass = model.class
    
      has_manys = klass.reflect_on_all_associations(:has_many)
      has_ones = klass.reflect_on_all_associations(:has_one)
      belong_tos = klass.reflect_on_all_associations(:belongs_to)
      habtms = klass.reflect_on_all_associations(:has_and_belongs_to_many)
    
      collection_associations = [has_manys, habtms].flatten
      instance_associations = [has_ones, belong_tos].flatten
    
      (collection_associations + instance_associations).each do |association|
        model.errors.delete(association.name)
      end
    
      collection_associations.each do |association|
        model.send(association.name).each do |child|
          next if child.valid?
          errors = child.errors.full_messages
          model.errors[:base] << "#{association.class_name} Invalid: #{errors.to_sentence}"
        end
      end
    
      instance_associations.each do |association|
        next unless child = model.send(association.name)
        next if child.valid?
        errors = child.errors.full_messages
        model.errors[:base] << "#{association.class_name} Invalid: #{errors.to_sentence}"
      end
    
      model.errors
    end
    
    0 讨论(0)
  • 2020-12-12 17:45

    I have the same issue. no good answer so far. So I solved it by myself. by replacing association error message with detail error message:

    create a concern file models/concerns/association_error_detail_concern.rb:

    module AssociationErrorDetailConcern
      extend ActiveSupport::Concern
    
      included do
        after_validation :replace_association_error_message
      end
    
      class_methods do
        def association_names
          @association_names ||= self.reflect_on_all_associations.map(&:name)
        end
      end
    
    
      def replace_association_error_message
        self.class.association_names.each do |attr|
          next unless errors[attr]
          errors.delete(attr)
          Array.wrap(public_send(attr)).each do |record|
            record.errors.full_messages.each do |message|
              errors.add(attr, message)
            end
          end
        end
      end
    end
    

    in your model:

    class School < ApplicationRecord
      include AssociationErrorDetailConcern
      has_many :students
      ...
    end
    

    then you will get you must supply a valid email error message on students attribute of school record. instead of useless message is invalid

    0 讨论(0)
  • 2020-12-12 17:45

    I see a problem in the posted code. add_student is a class method of class School, so self will point to the class object School instead of an instance object of class School. The line students << s will not add the record s to the record school because of this.

    I don't know if this is causing your error message problem, but I think this will keep the code from working properly.

    0 讨论(0)
  • 2020-12-12 17:49

    Update Rails 5.0.1

    You can use Active Record Autosave Association

    class School < ActiveRecord::Base
        has_many :students, autosave: true
        validates_associated :students
    end
    
    class Student < ActiveRecord::Base
        belongs_to :school
        validates_format_of :email,
                      :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
                      :message => "You must supply a valid email"
    end
    
    @school = School.new
    @school.build_student(email: 'xyz')
    @school.save
    @school.errors.full_messages ==> ['You must supply a valid email']
    

    reference: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

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