0
0

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.

find_or_initialize_byのびっくりした挙動を忘れないように残す

Posted at

この記事で書くこと

Railsガイド | find_or_initialize_by の挙動で、驚いたので忘れないように残す。
具体的には、 find_or_initialize_by の引数の値がnilだった時の挙動について書きます。

例示

params[:name] には nil が入る可能性がある。
User.name にはuniq制約がついている。

user = User.find_or_initialize_by(name: params[:name])

この時、どういったsqlが発行されるのか?

params[:name] がnilではなく、nameが引数のnameのレコードが既に存在している場合

# params[:name] は `test_user` だとする。
 User Load (1.1ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'test_user' LIMIT 1

存在するレコードが返る。

params[:name] がnilではなく、nameが引数のnameのレコードが存在していない場合

# params[:name] は `hoge` だとする。
User Load (2.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` = 'hoge' LIMIT 1

発行されるsqlは同じだが、返る値は、idを持たない、user.namehogeのレコード。

params[:name] がnilの場合 :exclamation: :exclamation:

# params[:name] は `nil` だとする。
User Load (2.7ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` IS NULL LIMIT 1

当たり前と言っては当たり前なのだが、nameに値が入ってないレコードの一件目が返る。
勝手にnilが入ったときには、エラーになるとか、既存のレコードは返却されずに新しいインスタンスが作成されて返されるとか都合の良い解釈を期待するが、そんなことは幻想。
全く意図しないレコードを更新してしまう可能性がある。

find_or_initialize_by の引数に意図せずnilが入る可能性がある処理を書くことは危ないことがわかった

実装の中身を読んでみると、

# File activerecord/lib/active_record/relation.rb, line 226
    def find_or_initialize_by(attributes, &block)
      find_by(attributes) || new(attributes, &block)
    end

とあり、まず、 nilだって立派な値なのだから find_by にだってしっかり入っていく。というのが理解できた。

find_or_create_by も同じく注意が必要。

# 意図してないけど、nilが入ってしまった。。。。。。。
new = User.find_or_create_by(name: nil)
User Load (5.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`name` IS NULL LIMIT 1

まとめ

「えっ!!何か知らないレコードが勝手に更新されてるんですが!!!!!」
を無くすためには、頭の中の勝手な都合の良い解釈をやめ、挙動をしっかり確認しよう。
nilは意図して使いたいとき以外は引数に入れないようにしようと思います。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?