给定此模型:
class Foo < ApplicationRecord
has_many :bars
accepts_nested_attributes_for :bars
end
class Bar < ApplicationRecord
validates :title, presence: true
end
我想获得我创建的每个嵌套Bar
的验证消息,因为…
Foo.create(bars_attributes: [, { title: nil }, { title: "foo" }, { title: nil }])
…结果是@errors=[#<ActiveModel::NestedError attribute=bars.title, type=blank, options={}>]
,但是我不知道哪些记录(在本例中是索引0和2的记录)失败了。这可能吗?
分解。而不是试图在一个命令中做所有的事情(它只能给你一个成功/失败的响应),尝试这样的东西(假设它被传递你所需的Bar记录的细节在参数中作为一个Enumerable(即数组或哈希))。
@foo = foo.create(field_1: foo_params[:field_1], field_2: foo_params[:field_2])
_failed_bar_records = []
foo_params[:bar_data].each do |_bar_data|
_bar = Bar.new(foo_id: @foo.id, title: _bar_data[:title])
_failed_bar_records.push(_bar_data[:title]) unless _bar.save
end
然后,您将有一个数组_failed_bar_records
,其中包含有关每个失败记录的数据。显然,您可以向该数组中添加更多内容,例如实际的错误消息。
您可以深入挖掘嵌套记录的错误,而不是检查父记录的错误
@problem = Problem.new(problem_params)
@problem.solutions
# [<Solution:0x0000565379c46170 id: nil, description: "a solution", problem_id: nil, ...>,
# <Solution:0x0000565379cc2248 id: nil, description: "", problem_id: nil, ...>]
@problem.save
@problem.errors
# <ActiveModel::Errors:..
# @base= <Problem:0x0000565379c33890 ...>,
# @errors=[
# <ActiveModel::NestedError attribute=solutions.description, type=blank, options={}>
# ]>
@problem.solutions.map(&:errors)
# [
# <ActiveModel::Errors:...
# @base= <Solution:0x0000565379c46170 ...>,
# @errors=[]>,
#
# <ActiveModel::Errors:...
# @base= <Solution:0x0000565379cc2248 ...>,
# @errors=[
# <ActiveModel::Error attribute=description, type=blank, options={}>
# ]>
# ]
详细解释Lam Phan的回答,每个孩子的验证错误都可以作为孩子本身的属性访问。
foo = Foo.create(bars_attributes: [{title: nil},{ title: "foo" }, {title: nil}])
=> #<Foo:0x0000559761e83c50 id: nil, created_at: nil, updated_at: nil>
foo.bars.map(&:errors).map(&:details)
=> [{:title=>[{:error=>:blank}]}, {}, {:title=>[{:error=>:blank}]}]
foo.bars.map(&:errors).map(&:messages)
=> [{:title=>["can't be blank"]}, {}, {:title=>["can't be blank"]}]
在另一个可能不那么优雅的路径上,使用自定义验证方法,可以循环接收到的嵌套记录,添加一个错误,注册失败记录的索引。
class Foo < ApplicationRecord
has_many :bars
accepts_nested_attributes_for :bars
validate :bars_validator
def bars_validator
children_valid = bars.map(&:valid?).all?
bars.each_with_index do |b,i|
b.errors.each do |err|
msg = "#{i}: #{err.attribute} #{err.message}"
errors.add(:bars, msg, index: i, src: err)
end
end
children_valid
end
end
然后通过errors.details
检索索引foo = Foo.create(bars_attributes: [{ title: nil }, { title: "foo" }, { title: nil }])
=> #<Foo:0x00005583f6607a50 id: nil, created_at: nil, updated_at: nil>
foo.errors.details
=>
{:"bars.title"=>[{:error=>:blank}],
:bars=>
[{:error=>"0: title can't be blank", :index=>0, :src=>#<ActiveModel::Error attribute=title, type=blank, options={}>},
{:error=>"2: title can't be blank", :index=>2, :src=>#<ActiveModel::Error attribute=title, type=blank, options={}>}]}