find_or_initialize_by と find_or_create_by の違い

find_or_create_by は引数に渡した条件のレコードがなければ create する。

find_or_initialize_by は引数に渡した条件のレコードがなければ new する。

返り値はレシーバのmodel。

引数に渡した条件は AND で検索されるので、このメソッドだけで create_or_update みたいに使うことはできない。

 Book.find_or_create_by(id: 5)
 # <Book id: 5, title: nil, created_at: "2020-09-28 04:13:23", updated_at: "2020-09-28 04:13:23">
 
 Book.find_or_create_by(id: 5, title: "fuga")
 # id:5, title: "fuga" のレコードはないのでcreateしようとするが、id重複でエラーになる
 ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: books.id)
 
 Book.find_or_initialize_by(id: 5, title: "fuga")
 # new するだけなのでエラーにならない
 
 Book.find_or_initialize_by(id: 5, title: "fuga").save
 # id:5, title: "fuga" のレコードはないのでcreateしようとするが、id重複でエラーになる
 ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: books.id)

レコードがなければ CREATE し、存在する場合は UPDATE という挙動にしたいなら、find_or_initialize_byupdate を組み合わせて使う

 # レコードがなければ INSERT になる
 book6 = Book.find_or_initialize_by(id: 6, title: "piyo")
 book6.update(title: "piyopiyo")
 # INSERT INTO "books" ("id", "title", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["id", 6], ["title", "piyopiyo"], ["created_at", "2020-09-28 04:21:17.337137"], ["updated_at", "2020-09-28 04:21:17.337137"]]
 
 # レコードがあれば UPDATE になる
 book6 = Book.find_or_initialize_by(id: 6)
 book6.update(title: "piyopiyopiyo")
 # UPDATE "books" SET "title" = ?, "updated_at" = ? WHERE "books"."id" = ?  [["title", "piyopiyopiyo"], ["updated_at", "2020-09-28 04:21:55.998472"], ["id", 6]]

参考

RailsのActiveRecordでcreate_or_updateを分岐なしでスマートにする方法 - ウェブエンジニア珍道中

ActiveRecord::Relation

ActiveRecordのsave()の挙動、あるいはcreate()とupdate()の挙動の違いについて - Qiita