0

関係を介して関連付けられている 2 つのモデルがあり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

新聞は 1 日に数回更新されますが、まだ存在していない記事を作成して関連付けに追加したいだけです。次のコードは、これを達成するための最初のカットでした。

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

新聞オブジェクトは、この時点で新しく保存されていないか、既存の関係で取得されています。

私のテストでは、上記のコードを使用して同じ UID を持つ 2 つの記事を新聞に追加できることが示されていますが、これは明らかに望ましくありません。

検証は関連ではなく、記事テーブル全体の一意性を調べるため、現在のコードを保存すると検証が失敗するように思われます。

私が理解するのに苦労しているのはexists?、このシナリオでメソッドがどのように動作するか (そして、なぜ計画どおりにベーコンが保存されないのか) です。FactoryGirl を使用して新聞を作成し、記事を追加してから、既に追加した記事と同じ uid を持つ記事を含む更新をシミュレートしています。コードが機能する場合、関連付けられた記事は 1 つしか取得できないはずですが、代わりに 2 つの記事が取得されます。いずれかbuildまたはどちらを使用してもcreate違いはありません。したがって、記事のレコードがデータベースに既に存在するかどうかによって、結果が変わるようには見えません。

望ましい結果を達成する方法や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の記事しか返されないからです。ただし、ここでUIDが一意であることが本当に懸念される場合は、新聞IDを不必要にフィルタリングするため、永続化されたケースもおそらく望ましくありません。

むしろ、次のように、関連付けを介してArticleスコーピングするのではなく、単に反対したい場合があります。exists?

unless Article.exists? uid: article.uid

あなたの他の問題に関して:

これはFactoryGirlの問題のようで、createメソッドがirbでできるのと同じ方法でdbエントリを作成していません。

FactoryGirl.createそれでも検証に従う必要があります。あなたのテストを見るのに役立つかもしれません。

于 2013-01-23T17:21:22.540 に答える