Rails:防止创建父模型并显示子模型验证错误

时间:2016-09-21 02:11:17

标签: ruby-on-rails validation activerecord

我一直坚持这个问题,并且已经彻底弄糊涂了嵌套模型和验证如何协同工作。

在下面的代码中,如果子模型(内容)的验证失败,我的目标是创建父模型(图像或视频)失败。目前,父模型正在保存,而子模型则没有,并且验证错误是闻所未闻的。如果没有验证错误,那么一切都按预期工作。

#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

非常感谢任何线索或见解!

3 个答案:

答案 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