我一直坚持这个问题,并且已经彻底弄糊涂了嵌套模型和验证如何协同工作。
在下面的代码中,如果子模型(内容)的验证失败,我的目标是创建父模型(图像或视频)失败。目前,父模型正在保存,而子模型则没有,并且验证错误是闻所未闻的。如果没有验证错误,那么一切都按预期工作。
#Image.rb
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
#Video.rb
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
#Content.rb
belongs_to :contentable,
inverse_of: :content,
polymorphic: true
validate :all_good?
def all_good?
errors.add(:base, "Nope!")
return false
end
非常感谢任何线索或见解!
答案 0 :(得分:5)
Rails有一个名为validates_associated的特殊验证,可确保关联的记录有效。如果关联的记录无效,则父记录也将无效,并且关联的错误将添加到其错误列表中。
在您的图片和视频课程中添加以下内容:
validates_associated :content
现在,如果content
关联无效,则不会保存视频或图片。
video = Video.new
video.content = Content.new
video.save #=> false
video.valid? #=> false
video.errors #=> [:content => "is invalid"]
答案 1 :(得分:4)
简答
添加到图片和视频模型:
accepts_nested_attributes_for :content
证明
我很确定我知道答案,但不确定它是否适用于多态关联(我之前没有使用过),所以我设置了一个小测试。
以与您的设置相同的方式创建模型,但使用name属性并使用可用于测试失败的验证。
class Image < ActiveRecord::Base
has_one :content,
as: :contentable,
inverse_of: :contentable,
dependent: :destroy
validates_length_of :name, maximum: 10
end
class Content < ActiveRecord::Base
belongs_to :contentable,
inverse_of: :content,
polymorphic: true
validates_length_of :name, maximum: 10
end
接下来将迁移设置为:
class CreateImages < ActiveRecord::Migration
def change
create_table :images do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreateContents < ActiveRecord::Migration
def change
create_table :contents do |t|
t.string :name
t.references :contentable, polymorphic: true, index: true
t.timestamps null: false
end
end
end
接下来编写一个RSpec来测试如果无法保存子项并且验证错误已经完成,则不会保存父项。
it 'should not save image if content is invalid' do
image = Image.new()
image.name = 'this is ok'
expect(image).to be_valid
content = Content.new()
content.name = 'a string that should fail validation'
image.content = content
expect(image).to_not be_valid
image.save
expect(image).to_not be_persisted
expect(content).to_not be_persisted
expect(image.errors.count).to eq(1)
expect(image.content.errors[:name][0]).to include('is too long')
end
跑完测试,确定它失败了。
接下来将以下行添加到图像(和视频)
accepts_nested_attributes_for :content
测试现在通过了 - 即,如果孩子验证失败,父母也将无法通过验证,并且不会保存。
答案 2 :(得分:0)
您需要在自定义验证中引发异常。 做点什么
before_save :ensure_all_good
def ensure_all_good
try saving stuff to chile
failed? raise nope
end