【问题标题】:Rails: Prevent parent model from being created and surfacing child model validation errorsRails:防止创建父模型并显示子模型验证错误
【发布时间】:2016-09-21 02:11:16
【问题描述】:

我被这个问题困扰了一段时间,对嵌套模型和验证如何协同工作感到非常困惑。

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

#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

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

【问题讨论】:

  • 您使用的是哪个版本的导轨?
  • @Dinesh Rails 4.2.6
  • 您到底为什么要这样设置-除非您提出最终目标,否则“推荐的方法”实际上并不可行-分离此类项目的确切原因是什么-模型/您的应用程序中的架构看起来像需要这种分离吗?
  • 我认为您的代码的重要部分丢失了。你必须向我们展示你如何坚持这些对象。例如,如果您首先创建 Image 并保存它,然后添加一个孩子,Rails 无法撤消此 save。然后你必须把每件事都包装在一个事务中并引发一个错误来触发回滚。
  • @slowjack2k 我实际上是在保存图像或视频,并在保存之前为内容设置关联。所以它之前没有被保存。

标签: ruby-on-rails validation activerecord


【解决方案1】:

Rails 有一个名为validates_associated 的特殊验证,可确保相关记录有效。如果关联记录无效,则父记录也将无效,并且关联错误将添加到其错误列表中。

在您的 Image 和 Video 类中添加以下内容:

validates_associated :content

现在如果content 关联无效,则不会保存视频或图像。

video = Video.new
video.content = Content.new
video.save #=> false
video.valid? #=> false
video.errors #=> [:content => "is invalid"]

【讨论】:

  • 感谢您抽出时间回复。不幸的是,这不起作用。它仍然继续保存记录(如果我插入validates_presence_of : content,那么它会说content cannot be blank)。嵌套会导致这种情况吗?另外,从文档中:“注意:如果尚未分配关联,则此验证不会失败。如果要确保关联既存在又保证有效,还需要使用 validates_presence_of”
【解决方案2】:

简答

添加到图像和视频模型:

accepts_nested_attributes_for :content

证明

我很确定我知道这个问题的答案,但不确定它是否适用于多态关联(我以前没有使用过),所以我设置了一个小测试。

以与设置相同的方式创建模型,但具有名称属性和可用于测试失败的验证。

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

测试现在通过 - 即,如果子验证失败,则父验证也将失败并且不会保存。

【讨论】:

    【解决方案3】:

    您需要在自定义验证中引发异常。 做类似的事情

    before_save :ensure_all_good
    
    def ensure_all_good
     try saving stuff to chile
     failed? raise nope
    end
    

    【讨论】:

    • 有趣的想法。我不应该只通过验证和错误对象来做到这一点,更传统的 rails 方式吗?
    • 我不会实现逻辑来更新我的验证中的孩子。因为那会误导。因此,为什么我更愿意使用 before_save 更新并在那里处理它。您始终可以将两个请求放在一个不会提交的事务中。
    • @geoboy 我刚刚遇到了一个类似的问题,来自子模型的错误不会传递给父模型。在您的情况下,因为验证在内容模型内。父模型视频和图像将无法从内容中看到错误。您可能想查看以下链接,建议您如何将错误从子模型合并到父模型stackoverflow.com/questions/2680016/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多