如何validationRails属于该关联的存在?

假设我有一个基本的Rails应用程序,它具有基本的一对多关系,其中每个评论都属于一篇文章:

$ rails blog $ cd blog $ script/generate model article name:string $ script/generate model comment article:belongs_to body:text 

现在我添加代码来创建关联,但我也想确保在创建注释时,它总是有一篇文章:

 class Article < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :article validates_presence_of :article_id end 

现在让我们说我想创建一篇带有评论的文章:

 $ rake db:migrate $ script/console 

如果你这样做:

 >> article = Article.new => #
>> article.comments.build => # >> article.save!

你会收到这个错误:

 ActiveRecord::RecordInvalid: Validation failed: Comments is invalid 

这是有道理的,因为评论还没有page_id。

 >> article.comments.first.errors.on(:article_id) => "can't be blank" 

因此,如果我从comment.rb删除validates_presence_of :article_id ,那么我可以执行保存,但这也可以让您创建没有文章ID的注释。 处理此问题的典型方法是什么?

更新 :根据尼古拉斯的建议,这里有一个save_with_comments的实现,但是很难看:

 def save_with_comments save_with_comments! rescue false end def save_with_comments! transaction do comments = self.comments.dup self.comments = [] save! comments.each do |c| c.article = self c.save! end end true end 

不确定我是否想为每个一对多关联添加这样的内容。 Andy可能是正确的,因为最好避免尝试进行级联保存并使用嵌套属性解决方案。 我会暂时搁置一下,看看是否有人有任何其他建议。

我也在调查这个话题,这是我的总结:

为什么这不起作用的根本原因OOTB(至少在使用validates_presence_of :article而不是validates_presence_of :article_id )是rails在内部不使用身份映射的事实,因此本身不会知道article.comments[x].article == article

我找到了三个变通方法,只需要一点点努力就可以了:

  1. 在创建注释之前保存文章(rails会自动将保存期间生成的文章ID传递给每个新创建的注释;请参阅Nicholas Hubbard的回复)
  2. 创建后明确地在评论上设置文章(参见W. Andrew Loe III的回复)
  3. 使用inverse_of:
     class Article < ActiveRecord::Base has_many :comments, :inverse_of => :article end 

最后一个解决方案是本文中提到的bot,但似乎是rails的快速修复解决方案,因为缺少身份映射。 它对我来说也是三个中最不具侵入性的一个。

你是对的。 在此validation工作之前,该文章需要一个id。 解决这个问题的一种方法是保存文章,如下所示:

 >> article = Article.new => #
>> article.save! => true >> article.comments.build => # >> article.save! => true

如果您在一个方法或操作中创建带有注释的新文章,那么我建议创建文章并保存它,然后创建注释,但将整个内容包装在Article.transaction块中,这样您就不会结束任何额外的文章。

您可以validation文章的存在,而不是validation文章ID的存在。

 validates_presence_of :article 

然后,当您创建评论时:

 article.comments.build :article => article 

我修复了此问题,将此后续行添加到我的_comment.html.erb:

如果form.object.new_record,“NEW”? %>

现在,validation以独立forms工作,也以多种forms工作。

这失败是因为Rails 2.3或3.0中没有身份映射。 您可以通过将它们拼接在一起来手动修复它。

 a = Article.new c = a.comments.build c.article = a a.save! 

这很糟糕,3.1中的身份图有助于修复(除了性能提升)。 c.articlea.comments.first.article是没有身份地图的不同对象。

如果您使用的是Rails 2.3,那么您正在使用新的嵌套模型。 我已经注意到你指出的validates_presence_of的相同失败,或者在该字段的迁移中指定了:null => false

如果您的目的是创建嵌套模型,则应将accepts_nested_attributes_for :comments添加到article.rb 。 这将允许您:

 a = Article.new a.comments_attributes = [{:body => "test"}] a.save! # creates a new Article and a new Comment with a body of "test" 

你拥有的是它应该对我有用的方式,但我发现它不适用于Rails 2.3.2。 我使用您的代码在注释中调试了before_create ,并且没有通过构建提供article_id (它是nil),所以这不起作用。 您需要首先保存文章,如Nicholas所指出,或删除validation。