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_by
と update
を組み合わせて使う
# レコードがなければ 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のsave()の挙動、あるいはcreate()とupdate()の挙動の違いについて - Qiita