Techioz Blog

Rails の関連付けが正しく関連付けられていません

概要

この目的は、コメントがユーザーに属する記事に属するようにすることです。

class User < ApplicationRecord
  has_one :profile
  has_many :articles, -> {order 'published_at DESC, title ASC'}, dependent: :nullify          
  has_many :replies, through: :articles, source: :comments
end

class Article < ApplicationRecord
  validates :title, :body, presence: true 

  belongs_to :user
  has_and_belongs_to_many :categories
  has_many :comments

  def long_title
    "#{title} - #{published_at}"
  end
end

class Comment < ApplicationRecord
  belongs_to :article
end

コンソールから返される内容は次のとおりです。

 irb(main):048> user.replies
=> []

しかし!

 irb(main):049> user.replies.all
  Comment Load (0.7ms)  SELECT "comments".* FROM "comments" INNER JOIN "articles" ON "comments"."article_id" = "articles"."id" WHERE "articles"."user_id" = ? ORDER BY published_at DESC, title ASC  [["user_id", 3]]
=> 
[#<Comment:0x0000ffff802d1420
  id: 1,
  article_id: 1,
  name: "guest",
  email: "[email protected]",
  body: "comment text",
  created_at: Tue, 03 Oct 2023 22:05:04.136672000 UTC +00:00,
  updated_at: Tue, 03 Oct 2023 22:05:04.136672000 UTC +00:00>]

user.replies が空であるのに .all が表示される理由を誰かが説明できますか。

Articles.last.comments だけを返すと (コメントと記事が 1 つだけあります)、コメントは正常に返されます。では、なぜ私がユーザーに要求したときに、記事はそれをユーザーに渡さないのでしょうか?

解決策

これはおそらく、ActiveRecord クエリのキャッシュが原因です。

コンソールでは、最初に user.replies を呼び出し、UI からコメントを追加してから、コンソールから user.replies を呼び出したことがあるでしょう。したがって、コンソールはキャッシュされた値を返しただけです。お気付きのとおり、結果が返される前に SQL ステートメント (つまり、Comment Load …) がありません。

user.replies.all を実行すると、SQL ステートメントが出力されることがわかります。

最初に user.reload を実行してから user.replies を実行すると、user.replies.all を呼び出した場合と同じ結果が得られるはずです。

おそらく、この問題はコンソールでのみ発生するでしょう。コンソールは初期テストには適していますが、リクエストの実際の動作を確認するには、ブラウザーまたは自動テスト、あるいはその両方でテストを実行するのが最善です。