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

时间:2021-12-02 00:19:08

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. Hypothetically, I have the following classes:

我很难理解如何让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

Now, in the controller, let's say we want to build a trivial API to allow us to add a new School with a student in it (again, I said, it's a terrible example, but plays its role for the purpose of the question)

现在,在控制器中,假设我们想构建一个简单的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

Now the validation is working just fine, things die because the email doesn't match the regex that's set in the validates_format_of method in the Student class. However the output I get is the following:

现在验证工作正常,因为电子邮件与学生类validates_format_of方法中设置的regex不匹配,所以出现了问题。然而,我得到的输出如下:

<?xml version="1.0" encoding="UTF-8"?>
<errors>
  <error>Students is invalid</error>
</errors>

I want the more meaningful error message that I set above with validates_format_of to show up. Meaning, I want it to say:

我希望上面使用validates_format_of设置的更有意义的错误消息出现。意思是,我想说

 <error>You must supply a valid email</error>

What am I doing wrong for that not to show up?

我做错了什么没有出现?

7 个解决方案

#1


68  

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:

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

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:

错误。add_to_base已从Rails 3.0及以上删除,应替换为:

errors[:base] << "Student Error: #{msg}"

#2


7  

Update Rails 5.0.1

更新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

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

#3


3  

This is not a public API yet, but Rails 5 stable seems to have ActiveModel::Errors#copy! to merge errors between two models.

这还不是一个公共API,但是Rails 5稳定看起来有ActiveModel:: error #copy!合并两个模型之间的错误。

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" ] 

Again, this is not officially published yet (I accidentally find this one before monkey-patching Errors class), and I'm not sure it will be.

同样,它还没有正式发布(我在monkey-patching Errors类之前偶然发现了这个),我不确定它是否会发布。

So it's up to you.

所以这取决于你。

#4


0  

You should use following in the rhtml.

您应该在rhtml中使用follow。

<%= error_messages_for :school, :student %>

To skip "Students is invalid" message use following in the student.rb

跳过“学生是无效的”信息在学生中使用。rb

  def after_validation
    # Skip errors that won't be useful to the end user
    filtered_errors = self.errors.reject{ |err| %w{ student}.include?(err.first) }
    self.errors.clear
    filtered_errors.each { |err| self.errors.add(*err) }
  end

EDITED

编辑

Sorry after_validation must be in a school.rb

#5


0  

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.

我在代码中看到一个问题。add_student是类School的一种类方法,因此self将指向类object School而不是类School的实例对象。因为这个原因,line学生<< s不会将记录s添加到记录学校。

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

我不知道这是否导致了您的错误消息问题,但是我认为这会阻止代码正常工作。

#6


0  

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:

我不确定这是否是最好的(或正确的)答案……我还在学习,但我发现这很有效。我还没有对它进行过广泛的测试,但它确实与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

#7


0  

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

#1


68  

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:

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

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:

错误。add_to_base已从Rails 3.0及以上删除,应替换为:

errors[:base] << "Student Error: #{msg}"

#2


7  

Update Rails 5.0.1

更新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

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

#3


3  

This is not a public API yet, but Rails 5 stable seems to have ActiveModel::Errors#copy! to merge errors between two models.

这还不是一个公共API,但是Rails 5稳定看起来有ActiveModel:: error #copy!合并两个模型之间的错误。

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" ] 

Again, this is not officially published yet (I accidentally find this one before monkey-patching Errors class), and I'm not sure it will be.

同样,它还没有正式发布(我在monkey-patching Errors类之前偶然发现了这个),我不确定它是否会发布。

So it's up to you.

所以这取决于你。

#4


0  

You should use following in the rhtml.

您应该在rhtml中使用follow。

<%= error_messages_for :school, :student %>

To skip "Students is invalid" message use following in the student.rb

跳过“学生是无效的”信息在学生中使用。rb

  def after_validation
    # Skip errors that won't be useful to the end user
    filtered_errors = self.errors.reject{ |err| %w{ student}.include?(err.first) }
    self.errors.clear
    filtered_errors.each { |err| self.errors.add(*err) }
  end

EDITED

编辑

Sorry after_validation must be in a school.rb

#5


0  

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.

我在代码中看到一个问题。add_student是类School的一种类方法,因此self将指向类object School而不是类School的实例对象。因为这个原因,line学生<< s不会将记录s添加到记录学校。

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

我不知道这是否导致了您的错误消息问题,但是我认为这会阻止代码正常工作。

#6


0  

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:

我不确定这是否是最好的(或正确的)答案……我还在学习,但我发现这很有效。我还没有对它进行过广泛的测试,但它确实与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

#7


0  

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