Ruby on rails RubyonRails:如何从显示的子资源获取错误消息?
我很难理解如何让Rails显示在呈现XML模板时验证失败的子资源的显式错误消息。假设我有以下课程:Ruby on rails RubyonRails:如何从显示的子资源获取错误消息?,ruby-on-rails,ruby,activerecord,rest,Ruby On Rails,Ruby,Activerecord,Rest,我很难理解如何让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 e
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
班级学校“您必须提供有效的电子邮件”
结束
现在,在控制器中,让我们假设我们想要构建一个简单的API,允许我们添加一个新的学校,其中有一个学生(同样,我说过,这是一个糟糕的例子,但在问题中扮演了它的角色)
class-SchoolsController@school.errors,:status=>:unprocessable_entity}
结束
结束
结束
结束
现在验证工作正常了,因为电子邮件与学生类中validates\u format\u方法中设置的正则表达式不匹配,所以一切都会消失。但是,我得到的输出如下:
<?xml version="1.0" encoding="UTF-8"?>
<errors>
<error>Students is invalid</error>
</errors>
学生是无效的
我想显示我在上面用validates\u format\u设置的更有意义的错误消息。意思是,我想说:
<error>You must supply a valid email</error>
您必须提供有效的电子邮件
我做了什么错事让它不出现 您应该在rhtml中使用以下命令
<%= error_messages_for :school, :student %>
已编辑
Sorry after_validation must be in a school.rb
我在发布的代码中看到一个问题
add_student
是classSchool
的类方法,因此self
将指向class对象School
,而不是classSchool
的实例对象。行students在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
注意:
向学校添加新学生不需要单独的方法,请使用以下语法:
school.students.build(:email => email)
Rails 3.0的更新+
错误。已从Rails 3.0及更高版本中删除“将\添加到\基础”
,应替换为:
errors[:base] << "Student Error: #{msg}"
errors[:base]我不确定这是否是最好(或正确)的答案……我仍在学习,但我发现这很有效。我还没有对它进行过广泛的测试,但它似乎确实适用于Rails 4:
validate do |school|
school.errors.delete(:students)
school.students.each do |student|
next if student.valid?
school.errors.add(:students, student.errors)
end
end
下面是一个可以经受一些干燥的示例:
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
def join_model_和_association_错误!(模型)
klass=model.class
has_manys=klass.反思所有关联(:has_many)
has_ones=klass。反映所有关联(:has_one)
归属=klass。反映所有关联(:归属)
habtms=klass。反映所有关联(:has_和属于许多)
集合\u关联=[has\u manys,habtms]。展平
实例\关联=[有\个,属于\ tos]。展平
(集合关联+实例关联)。每个集合关联|
model.errors.delete(association.name)
结束
集合|关联。每个do |关联|
model.send(association.name)|
下一个如果child.valid?
errors=child.errors.full_消息
model.errors[:base]这还不是一个公共API,但Rails 5 stable似乎有ActiveModel::errors#copy
在两个模型之间合并错误
user=user.new(名称:“foo”,电子邮件:nil)
其他=用户。新建(名称:无,电子邮件:foo@bar.com")
user.errors.copy!(其他.错误)
user.full_messages#=>[“名称为空”,“电子邮件为空”]
同样,这篇文章还没有正式发表(我在monkey patchingErrors
class之前意外地发现了这篇文章),我不确定它会不会发表
所以这取决于你 更新Rails 5.0.1
您可以使用活动记录自动保存关联
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']
班级学校/^([^@\s]+)@((?:[-a-z0-9]+\)+[a-z]{2,})$/i,
:message=>“您必须提供有效的电子邮件”
结束
@学校
@学校。建筑学生(电子邮件:“xyz”)
@学校储蓄
@school.errors.full_messages==>[“您必须提供有效的电子邮件”]
参考资料:我也有同样的问题。到目前为止还没有好的答案。
所以我自己解决了。通过将关联错误消息替换为详细错误消息:
创建关注点文件模型/关注点/关联\u错误\u详细信息\u关注点.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
在您的模型中:
class School < ApplicationRecord
include AssociationErrorDetailConcern
has_many :students
...
end
班级学校
然后您将获得您必须在学校
记录的学生
属性上提供有效的电子邮件
错误消息。在XML模板中执行此操作的最佳方法是什么?现在我没有一个XML视图,我只是让render来处理它,它被链接并存储到数据库中。正如问题最初所暗示的那样,我故意通过发送一封不好的电子邮件而使验证失败。如果我发送了一封通过正则表达式的正确电子邮件,那么我就不会收到错误。这里的要点是故意失败,并在视图中获得适当的信息。我知道您故意失败了示例。我想你不明白我的意思。为什么要将add_student方法作为class方法?在Ruby中,self指向class方法中的class对象,因此消息add_student在发送到实例对象时,执行时self指向class对象而不是实例对象。尽管如此,也许ActiveRecord成功地做了正确的事情,但除非我遗漏了什么,否则该方法应该
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']
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
class School < ApplicationRecord
include AssociationErrorDetailConcern
has_many :students
...
end