如果一条记录无效,Rails accepted_nested_attributes_for将不保存任何嵌套记录

时间:2014-03-13 22:08:18

标签: ruby-on-rails nested-attributes

如果其中一个记录未通过验证,则创建具有嵌套关联的记录将无法保存任何关联的记录。

class Podcast < ActiveRecord::Base
  has_many :episodes, inverse_of: :podcast
  accepts_nested_attributes_for :episodes
end

class Episode < ActiveRecord::Base
  belongs_to :podcast, inverse_of: :episodes
  validates :podcast, :some_attr, presence: true
end

# Creates a podcast with one episode.
case_1 = Podcast.create {
  title: 'title'
  episode_attributes: [
    {title: "ep1", some_attr: "some_attr"}, # <- Valid Episode
  ]
}

# Creates a podcast without any episodes.
case_2 = Podcast.create {
  title: 'title'
  episode_attributes: [
    {title: "ep1", some_attr: "some_attr"}, # <- Valid Episode
    {title: "ep2"}                          # <- Invalid Episode
  ]
}

我希望case_1能够成功保存一个已创建的剧集。 我希望case_2做两件事之一:

  • 一集保存
  • 无法保存并出现验证错误。

相反,播客会保存,但这两集都没有。

我希望播客能够保存任何有效的剧集。

我想通过将接受嵌套属性行更改为

来拒绝无效剧集
accepts_nested_attributes_for :episodes, reject_if: proc { |attributes| !Episode.new(attributes).valid? }

但每一集都无效,因为他们还没有podcast_id,所以他们会失败validates :podcast, presence: true

3 个答案:

答案 0 :(得分:2)

尝试此模式:在accepts_nested_attributes_for指令(docs)中使用:reject_if param并传递方法以发现属性是否有效。这样您就可以将验证卸载到Episode模型中。

像...一样的东西。

accepts_nested_attributes_for :episodes, :reject_if => :reject_episode_attributes?
def reject_episode_attributes?( attributes )
    !Episode.attributes_are_valid?( attributes )
end

然后在剧集中你制作了一个测试你喜欢的方法。您甚至可以创建新记录并使用现有验证。

def self.attributes_are_valid?( attributes )
    new_e = Episode.new( attributes )
    new_e.valid?
end

答案 1 :(得分:1)

您可以使用validates_associated导致第二个选项(无法保存并显示验证错误)

class Podcast < ActiveRecord::Base
  has_many :episodes, inverse_of: :podcast
  validates_associated :episodes
  accepts_nested_attributes_for :episodes
end

更新:

要做选项一(保存一集)你可以这样做: 1.添加validates_associated:剧集 2.在保存@podcast失败后,在控制器的创建操作中添加代码。首先,检查@ podcast.errors对象,看看故障是否是由剧集的验证错误引起的(只有那个),否则正常处理。如果由剧集上的验证错误引起,那么请执行@ podcast.episodes.each {| e | @ podcast.episodes.delete(e)除非e.errors.empty?}然后再次保存。

这看起来像是:

def create
 @podcast = Podcast.new(params[:podcast])
 if @podcast.save
   redirect_to @podcast
 else
   if #some conditions looking at @podcast.errors to see that it's failed because of the validates episodes
     @podcast.episodes.each do |episode|
       @podcast.episodes.delete(episode) unless episode.errors.empty?
     end
     if @podcast.save
       redirect_to @podcast
     else
       render :new
     end
   else
     render :new
   end
 end
end

答案 2 :(得分:0)

要获得第一个选项,请尝试为您的剧集启用自动保护功能。

class Podcast < ActiveRecord::Base
  has_many :episodes, inverse_of: :podcast, autosave: true
  accepts_nested_attributes_for :episodes
end