如何验证与 Rails 的关联是否存在? [英] How can you validate the presence of a belongs to association with Rails?

查看:52
本文介绍了如何验证与 Rails 的关联是否存在?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Say I have a basic Rails app with a basic one-to-many relationship where each comment belongs to an article:

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

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

Now I add in the code to create the associations, but I also want to be sure that when I create a comment, it always has an article:

class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
  validates_presence_of :article_id
end

那么现在假设我想一次性创建一篇带有评论的文章:

So now let's say I'd like to create an article with a comment all at once:

$ rake db:migrate
$ script/console

如果你这样做:

>> article = Article.new
=> #<Article id: nil, name: nil, created_at: nil, updated_at: nil>
>> article.comments.build
=> #<Comment id: nil, article_id: nil, body: nil, created_at: nil, updated_at: nil>
>> article.save!

你会得到这个错误:

ActiveRecord::RecordInvalid: Validation failed: Comments is invalid

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

Which makes sense, because the comment has no page_id yet.

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

因此,如果我从 comment.rb 中删除 validates_presence_of :article_id,那么我可以进行保存,但这也将允许您创建没有文章 ID 的评论.处理此问题的典型方法是什么?

So if I remove the validates_presence_of :article_id from comment.rb, then I could do the save, but that would also allow you to create comments without an article id. What's the typical way of handling this?

更新:根据 Nicholas 的建议,这里有一个 save_with_comments 的实现,它有效但很丑陋:

UPDATE: Based on Nicholas' suggestion, here's a implementation of save_with_comments that works but is ugly:

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

不确定是否要为每个一对多关联添加类似的内容.安迪可能是正确的,因为最好避免尝试进行级联保存并使用嵌套属性解决方案.我会让这个开放一段时间,看看是否有人有任何其他建议.

Not sure I want add something like this for every one-to-many association. Andy is probably correct in that is just best to avoid trying to do a cascading save and use the nested attributes solution. I'll leave this open for a while to see if anyone has any other suggestions.

推荐答案

我也一直在研究这个话题,这里是我的总结:

I've also been investigating this topic and here is my summary:

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

The root cause why this doesn't work OOTB (at least when using validates_presence_of :article and not validates_presence_of :article_id) is the fact that rails doesn't use an identity map internally and therefore will not by itself know that article.comments[x].article == article

我找到了三种解决方法,只需稍加努力即可:

I have found three workarounds to make it work with a little effort:

  1. 在创建评论之前保存文章(Rails 会自动将保存期间生成的文章 ID 传递给每个新创建的评论;请参阅 Nicholas Hubbard 的回复)
  2. 在创建评论后显式设置文章(参见 W. Andrew Loe III 的回复)
  3. 使用inverse_of:
  1. Save the article before creating the comments (rails will automatically pass the article id that was generated during the save to each newly created comments; see Nicholas Hubbard's response)
  2. Explicitly set the article on the comment after creating it (see W. Andrew Loe III's response)
  3. Use inverse_of:
class Article < ActiveRecord::Base
  has_many :comments, :inverse_of => :article
end

本文中还提到了 bot 的最后一个解决方案,但似乎是 Rails 针对缺少身份映射的快速修复解决方案.对我来说,它也是三者中干扰最少的一种.

This last solution was bot yet mentioned in this article but seems to be rails' quick fix solution for the lack of an identity map. It also looks the least intrusive one of the three to me.

这篇关于如何验证与 Rails 的关联是否存在?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆