Help us understand the problem. What is going on with this article?

【Rails】has_manyをhas_oneに変えた時の挙動

has_manyで複数のレコードが紐づいている状態で、has_oneに変えるとどのレコードが紐づくのかふと気になったので試してみた。
言い換えると、has_many関係でarticle.commentsが複数取れる状況だった時に、has_oneに変更してarticle.commentを実行すると、どのレコードを取ってくるのかを見てみた。

結論

created_at, updated_atの値に関係なく、idの一番若いレコードと紐づく

一番新しいレコードを取ってくるのかなぁと思ったらそうではなかった。
上の結論に至るまでの検証を下にまとめていく。

前提

ArticleモデルとCommentモデルを用意し、Article has_many Commentsの関係をとるとする。

article.rb
class Article < ApplicationRecord
  has_many :comments
  # has_one :comment #検証用
end
comment.rb
class Comment < ApplicationRecord
  belongs_to :article
end

用意したレコードはarticle1個comment3個。commentは全てarticleに紐づいてるものとする。

article
#<Article:0x00007f979bad9f00
 id: 1,
 title: "title1",
 created_at: Tue, 03 Dec 2019 04:54:13 UTC +00:00,
 updated_at: Tue, 03 Dec 2019 04:54:13 UTC +00:00>
comment
[#<Comment:0x00007f9798e79f28
  id: 1,
  content: "comment1",
  article_id: 1,
  created_at: Tue, 03 Dec 2019 04:55:15 UTC +00:00,
  updated_at: Tue, 03 Dec 2019 04:55:15 UTC +00:00>,
 #<Comment:0x00007f9798e79d98
  id: 2,
  content: "comment2",
  article_id: 1,
  created_at: Tue, 03 Dec 2019 04:55:23 UTC +00:00,
  updated_at: Tue, 03 Dec 2019 04:55:23 UTC +00:00>,
 #<Comment:0x00007f9798e79c08
  id: 3,
  content: "comment3",
  article_id: 1,
  created_at: Tue, 03 Dec 2019 04:55:26 UTC +00:00,
  updated_at: Tue, 03 Dec 2019 04:55:26 UTC +00:00>]

検証1 上記条件でhas_many => has_oneに変更

上記の状態で article.rb の has_many をコメントアウトし、 has_oneに変更
Article.first.comment実行

[1] pry(main)> Article.first.comment

結果

id = 1のcomment

result
=> #<Comment:0x00007f979b488638
 id: 1,
 content: "comment1",
 article_id: 1,
 created_at: Tue, 03 Dec 2019 04:55:15 UTC +00:00,
 updated_at: Tue, 03 Dec 2019 04:55:15 UTC +00:00>

=> 一番若いidを取ってくる

結論の通り。だけどこれだけではupdated_atとcreated_atの影響の可能性も捨てきれないので、もうちょい掘り下げる。

検証2 update_atの値を変える

id=1のcommentをアップデートし、updated_atを他二つより最近のものにする
has_oneに変更し、Article.first.comment実行

[1] pry(main)> Comment.first.update_attributes(content: "comment1A")
[2] pry(main)> Article.first.comment

結果

id = 1のcomment

result
=> #<Comment:0x00007f979b675568
 id: 1,
 content: "comment1A",
 article_id: 1,
 created_at: Tue, 03 Dec 2019 04:55:15 UTC +00:00,
 updated_at: Tue, 03 Dec 2019 04:59:09 UTC +00:00>

=> updated_atの値に関係なく一番若いidを取ってくる
updated_atの値を見てわかる通り。

検証3 id=1 のCommentレコードを削除

id=1のcommentを削除
has_oneに変更し、同様に実行

[1] pry(main)> Comment.first.delete
[2] pry(main)> Article.first.comment

結果

id = 2のcomment

result
=> #<Comment:0x00007f979bbe3540
 id: 2,
 content: "comment2",
 article_id: 1,
 created_at: Tue, 03 Dec 2019 04:55:23 UTC +00:00,
 updated_at: Tue, 03 Dec 2019 04:55:23 UTC +00:00>

=> 存在しているレコードの中で一番若いidを取ってくる
commentのidが2と3しかない状態なのでこのようになる

検証4 再び id=1 のCommentレコードを追加

検証3を終えた状態 (id=1のcommentを消した状態) で
id=1のcommentを再びcreate。articleに紐づける
has_oneに変更し、同様に実行

[1] pry(main)> Comment.create(id:1, content:"comment1", article_id:1)
[2] pry(main)> Article.first.comment

結果

id = 1のcomment

result
=> #<Comment:0x00007f979b88a678
 id: 1,
 content: "comment1",
 article_id: 1,
 created_at: Tue, 03 Dec 2019 05:05:59 UTC +00:00,
 updated_at: Tue, 03 Dec 2019 05:05:59 UTC +00:00>

=> created_atの値に関係なく一番若いidを取ってくる
今までは"idが一番若い = created_atの値が一番古い"が成り立ってたたため、これでcreated_atの値が関係ないことがわかった。
一度消したのでid=2をとってくるかと思いきやid=1だった。

終わりに

結論の通り、has_manyからhas_oneに変更した場合、idの一番若いものを取ってくることがわかった。直感的には一番新しいレコードを取ってきそうな気はするけど、そうではないっぽいので気をつけたい。検証不足、間違い等ありましたらコメントお願いしますm(_ _)m

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした