如何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
我找到了三个变通方法,只需要一点点努力就可以了:
- 在创建注释之前保存文章(rails会自动将保存期间生成的文章ID传递给每个新创建的注释;请参阅Nicholas Hubbard的回复)
- 创建后明确地在评论上设置文章(参见W. Andrew Loe III的回复)
- 使用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.article
和a.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。