find_or_initialize_byを使ってみる

要約

find_or_initialize_byはRailsのメソッドで、同時にfind_byとnewを行ってくれる。

find_or_initialize_by | Railsドキュメント

モデル.find_or_initialize_by(条件)

find_byで検索してない場合、条件に入れた値を基に新しいインスタンスが生成される。 createではなくnewなので、saveしないと保存されない。

Userモデルにid=1のレコードが一個入ってるテーブルで以下実行してみた。

レコードが存在する場合:idで検索
irb(main):001:0> new_user = User.find_or_initialize_by(id:1)
  User Load (7.0ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
irb(main):002:0> new_user
=> #<User id: 1, provider: "email", uid: "test@example.com", allow_password_change: false, name: "テストユーザー", nickname: nil, image: nil, email: "test-user+1@example.com", created_at: "2021-12-12 18:16:56", updated_at: "2021-12-14 08:00:35">

レコードが存在する場合:id以外で検索
irb(main):011:0> new_user = User.find_or_initialize_by(uid:'test@example.com')
  User Load (2.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`uid` = 'test@example.com' LIMIT 1
irb(main):012:0> new_user
=> #<User id: 1, provider: "email", uid: "test@example.com", allow_password_change: false, name: "テストユーザー", nickname: nil, image: nil, email: "test-user+1@example.com", created_at: "2021-12-12 18:16:56", updated_at: "2021-12-14 08:00:35">


レコードが存在しない場合:idで検索
irb(main):003:0> new_user = User.find_or_initialize_by(id:2)
  User Load (2.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
irb(main):004:0> new_user
=> #<User id: 2, provider: "email", uid: "", allow_password_change: false, name: nil, nickname: nil, image: nil, email: nil, created_at: nil, updated_at: nil>

レコードが存在しない場合saveする。validationが走る。
irb(main):005:0> new_user.save!
   (1.3ms)  BEGIN
  User Exists? (3.7ms)  SELECT 1 AS one FROM `users` WHERE `users`.`email` IS NULL AND `users`.`provider` = 'email' LIMIT 1
   (1.3ms)  ROLLBACK
Traceback (most recent call last):
        1: from (irb):5
ActiveRecord::RecordInvalid (Validation failed: Password can't be blank, Email can't be blank)


レコードが存在しない場合:id以外で検索。検索条件を代入してインスタンス生成される。
irb(main):009:0> new_user = User.find_or_initialize_by(email:'test@example.com')
  User Load (0.9ms)  SELECT `users`.* FROM `users` WHERE `users`.`email` = 'test@example.com' LIMIT 1
irb(main):010:0> new_user
=> #<User id: nil, provider: "email", uid: "", allow_password_change: false, name: nil, nickname: nil, image: nil, email: "test@example.com", created_at: nil, updated_at: nil>