Ruby on Rails:如何从显示的子资源中获取错误消息?

ran*_*its 42 ruby rest activerecord ruby-on-rails

我很难理解如何让Rails为渲染XML模板时验证失败的子资源显示明确的错误消息.假设我有以下几个类:

class School < ActiveRecord::Base
    has_many :students
    validates_associated :students

    def self.add_student(bad_email)
      s = Student.new(bad_email)
      students << s
    end
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
Run Code Online (Sandbox Code Playgroud)

现在,在控制器中,假设我们想构建一个简单的API,允许我们在其中添加一个带有学生的新学校(再次,我说,这是一个可怕的例子,但为了问题的目的发挥作用)

class SchoolsController < ApplicationController
    def create
      @school = School.new
      @school.add_student(params[:bad_email])
      respond_to do |format|
          if @school.save
          # some code
          else
            format.xml  { render :xml => @school.errors, :status => :unprocessable_entity }
          end
      end
    end
end
Run Code Online (Sandbox Code Playgroud)

现在验证工作正常,因为电子邮件与Student类中的validates_format_of方法中设置的正则表达不匹配而死亡.但是我得到的输出如下:

<?xml version="1.0" encoding="UTF-8"?>
<errors>
  <error>Students is invalid</error>
</errors>
Run Code Online (Sandbox Code Playgroud)

我想要上面设置的更有意义的错误消息,并显示validates_format_of.意思是,我想说:

 <error>You must supply a valid email</error>
Run Code Online (Sandbox Code Playgroud)

为了不出现,我做错了什么?

Har*_*tty 75

School模型中添加验证块以合并错误:

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
Run Code Online (Sandbox Code Playgroud)

现在@school.errors将包含正确的错误:

format.xml  { render :xml => @school.errors, :status => :unprocessable_entity }
Run Code Online (Sandbox Code Playgroud)

注意:

您不需要使用单独的方法将新学生添加到学校,请使用以下语法:

school.students.build(:email => email)
Run Code Online (Sandbox Code Playgroud)

Rails 3.0+的更新

errors.add_to_base 已从Rails 3.0及更高版本中删除,应替换为:

errors[:base] << "Student Error: #{msg}"
Run Code Online (Sandbox Code Playgroud)

  • 我们如何使这个验证方法一般化,以便我不必为我的模型中的每个关联定义它? (2认同)

Ash*_*wal 8

更新Rails 5.0.1

您可以使用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']
Run Code Online (Sandbox Code Playgroud)

参考:http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html

  • 这也适用于rails 4.2.`validates_associated:students`是多余的,因为默认情况下自动保存的关联验证子项,除非声明`validate:false`. (3认同)
  • 这会验证关联的学生,但当您有多个验证时,它不会区分哪些验证失败,它只会显示“学生无效”。 (2认同)

Quv*_*Quv 7

这还不是一个公共API,但Rails 5 stable似乎必须在两个模型之间ActiveModel::Errors#copy!合并errors.

user  = User.new(name: "foo", email: nil)
other = User.new(name: nil, email:"foo@bar.com")

user.errors.copy!(other.errors)
user.full_messages #=> [ "name is blank", "email is blank" ] 
Run Code Online (Sandbox Code Playgroud)

再次,这还没有正式发布(我在猴子修补Errors课之前偶然发现了这个),我不确定它会是什么.

所以这取决于你.