5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-03

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

5
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?