0

我有两个通过has_many关系关联的模型。例如

class Newspaper < ActiveRecord::Base
  has_many :articles
end

class Article < ActiveRecord::Base
  belongs_to :newspaper

  validates :uid, presence: true,
                  uniqueness: { case_sensitive: true }
end

一份报纸每天更新几次,但我们只想构建和添加不存在的文章到关联中。以下代码是我实现这一目标的第一步。

new_articles.each do |article|
  unless newspaper.articles.exists? uid: article.uid
    newspaper.articles.build(uid: article.uid)
  end
end

报纸对象要么是新的且未保存,要么是在此时使用现有关系检索到的。

我的测试表明,我可以使用上面的代码向报纸添加具有相同 UID 的两篇文章,这显然不是我想要的。

在我看来,我当前的代码在保存时会导致验证失败,因为验证着眼于整个文章表的唯一性,而不是关联

我正在努力理解的是该exists?方法在这种情况下的行为方式(以及为什么它没有按计划保存我的培根)。我正在使用 FactoryGirl 构建一份报纸,添加一篇文章,然后模拟包含与我已经添加的文章具有相同 uid 的文章的更新。如果代码有效,我应该只得到一篇相关的文章,但我得到了两篇。使用其中之一buildcreate没有区别,因此文章记录是否已经存在于数据库中似乎不会改变结果。

谁能阐明我如何才能达到预期的结果,或者为什么该exists?方法没有达到我的预期?

谢谢

4

1 回答 1

0

根据关联,该关联exists?实际上创建了一个范围查询。这就是您现有的文章过滤器不起作用的原因。

unless newspaper.articles.exists? uid: article.uid

# `articles.exists?` here will produce this if the newspaper is new 
#   ... WHERE "articles"."newspaper_id" IS NULL AND "articles.uid" = '<your uid>'

# and this, if the newspaper is persisted (with an id of 1)
#   ... WHERE "articles"."newspaper_id" = 1 AND "articles.uid" = '<your uid>'

新报纸的案例显然是错误的,因为它只会返回带有nil报纸 ID 的文章。但是持久化的情况也可能是不可取的,因为它仍然不必要地过滤报纸 ID,而您真正关心的是 UID 是唯一的。

相反,您可能只想简单地反对Article,而不是exists?通过关联来确定范围,例如:

unless Article.exists? uid: article.uid

关于您的其他问题:

这似乎是一个 FactoryGirl 问题,其中 create 方法没有像在 irb 中那样创建数据库条目。

FactoryGirl.create仍应遵守验证。查看您的测试可能会有所帮助。

于 2013-01-23T17:21:22.540 回答