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
さて、コントローラーで、生徒がいる新しい学校を追加できるようにするための簡単な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
これで検証は問題なく機能しますが、メールがStudentクラスのvalidates_format_ofメソッドで設定された正規表現と一致しないため、問題が発生します。しかし、私が得る出力は次のとおりです:
<?xml version="1.0" encoding="UTF-8"?>
<errors>
<error>Students is invalid</error>
</errors>
上記でvalidates_format_ofを使用して設定したより意味のあるエラーメッセージが表示されるようにします。意味、私はそれを言ってほしい:
<error>You must supply a valid email</error>
それが表示されないのはなぜですか。
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.errors
には正しいエラーが含まれます:
format.xml { render :xml => @school.errors, :status => :unprocessable_entity }
注:
新しい生徒を学校に追加するための個別の方法は必要ありません。次の構文を使用します。
school.students.build(:email => email)
errors.add_to_base
はRails 3.0以降)から削除されているため、次のものに置き換える必要があります。
errors[:base] << "Student Error: #{msg}"
更新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']
参照: http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html
これはまだ公開APIではありませんが、Rails 5安定版はActiveModel::Errors#copy!
2つのモデル間でerrors
をマージします。
user = User.new(name: "foo", email: nil)
other = User.new(name: nil, email:"[email protected]")
user.errors.copy!(other.errors)
user.full_messages #=> [ "name is blank", "email is blank" ]
繰り返しますが、これはまだ公式に公開されていません(モンキーパッチErrors
クラスの前に誤ってこれを見つけました)。
だから、それはあなた次第です。
これが最良の(または正しい)答えかどうかはわかりません...まだ学習中ですが、かなりうまくいくことがわかりました。私はそれを広範囲にテストしていませんが、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
Rhtmlでは以下を使用する必要があります。
<%= error_messages_for :school, :student %>
"Students is invalid"メッセージをスキップするには、student.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
[〜#〜]編集済み[〜#〜]
Sorry after_validation must be in a school.rb
これは、ある程度の乾燥に耐えられる例です。
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
同じ問題があります。これまでのところ良い答えはありません。自分で解決しました。アソシエーションエラーメッセージを詳細エラーメッセージで置き換えることにより:
懸念事項ファイルを作成する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
モデルで:
class School < ApplicationRecord
include AssociationErrorDetailConcern
has_many :students
...
end
その後、you must supply a valid email
students
レコードのschool
属性に関するエラーメッセージ。役に立たないメッセージの代わりにis invalid
投稿されたコードに問題があります。 add_student
は、クラスSchool
のクラスメソッドであるため、self
は、クラスSchool
のインスタンスオブジェクトではなく、クラスオブジェクトSchool
を指します。 。このため、行students << s
はレコードs
をレコードschool
に追加しません。
これがエラーメッセージの問題の原因かどうかはわかりませんが、これによりコードが正しく機能しなくなると思います。