Rails3のメソッド紹介(1) ActiveRecord::Relation#first_or_create

少しずつRailsの便利なメソッドを紹介してみようと思い立ったので書く.
今回はタイトルどおりActiveRecord::Relation#first_or_createというメソッドを紹介.
(後々気づいたけど,Rails4から非推奨になるらしい.代わりにfind_or_create_byなるメソッドを使うらしい)

このメソッドはメソッド名通りで,ActiveRecordで一番最初に見つかったものを返す.
もし見つからなければ指定した引数にしたがって新しいレコードを作成する.

ソースコードを見てみてもそのまんま

def first_or_create(attributes = nil, options = {}, &block)
  first || create(attributes, options, &block)
end

最初にfirstを実行し,見つからなければcreateを実行している.

簡単な実行例

例えばid, name, email, ageを持つUserモデルが以下のようになっているとし,現在以下のようにデータベースにユーザ情報が保存されているとする.

id name email age
1 藤井隆 fujii@example.com 25
2 山田太郎 yamada@example.com 20
3 山本健太 yamamoto@example.com 20

この時,次のコードを実行すると…

User.where(:age => 20).first_or_create(
  :name => "加藤拓哉",
  :email => "kato@example.com",
  :age => 30
)
#=> <User id: 2, name: '山田太郎', email: 'yamada@example.com', age: 20>

id==2のレコードが取得されている.
この場合はまず,whereでUserテーブルからageが20のものを取得し,その中で一番最初のものを見つけるので,結果はid==2のレコードが取得できる.

次のコードを実行すると…

User.where(:age => 30).first_or_create(
  :name => "加藤拓哉",
  :email => "kato@example.com"
)
#=> <User id: 4, name: '加藤拓哉', email: 'kato@example.com', age: 30>

新しくid==4のレコードが取得されている. この場合はまず,whereでUserテーブルからageが30のものを取得するが,テーブル上にひとつもない.
その上でfirst_or_createが実行されるが,先ほどのwhereでレコードがひとつも見つからなかったので,first_or_createの引数で指定した内容のUserが新しく作成され,作成したUserのレコード情報が返ってくる. 注目すべきは,ageに30が入っている点.
実はfirst_or_create以前にwhereで絞り込んだ値を自動的に使ってくれます.

また,createされたので当然データベースも更新されていて,

id name email age
1 藤井隆 fujii@example.com 25
2 山田太郎 yamada@example.com 20
3 山本健太 yamamoto@example.com 20
4 加藤拓哉 kato@example.com 30

となるはずである.

実行例(応用編)

データベースは先ほどと同様に以下のとおりとする.

id name email age
1 藤井隆 fujii@example.com 25
2 山田太郎 yamada@example.com 20
3 山本健太 yamamoto@example.com 20

first_or_createはブロックを渡すことができ,例えば次のコードのようにかけます.

例1

User.where(:age => 20).first_or_create do |user|
  user.name = "前田剛"
  user.email = "maeda@example.com"
end
#=> <User id: 2, name: '山田太郎', email: 'yamada@example.com', age: 20>

例2

User.where(:age => 30).first_or_create do |user|
  user.name = "前田剛"
  user.email = "maeda@example.com"
end
#=> <User id: 4, name: '前田剛', email: 'maeda@example.com', age: 30>

例1はageが20のレコードが存在するので,ageが20のレコードのうち一番先頭のid==2のものを取得している.
このとき,すでにレコードが見つかっているのでブロックの中身は無視される.

例2も上記の基本の実行例と同じくageが30のものを探すが,見つからないので新しくid==4の前田さんのレコードを作成している.
こちらもfirst_or_create以前にwhereで絞り込んだ条件を使ってくれて,ageに30がちゃんと入っています.

その他

  • first_or_create!メソッドもある.こちらはfirstで見つからないときにcreateではなくcreate!を使う.つまり作成に失敗すると例外が発生します.
  • より詳細はRailsのドキュメントを参照

まとめ

  • first_or_createを使うとDBにあればそれを使い,なければ新しく作るみたいなことが綺麗にかける
  • first_or_createはで新しくレコードを作成するときは,ActiveRecordのwhereで絞り込んだ属性をデフォルトで自動的に使ってくれる
  • Rails4からは非推奨で代わりに,find_or_create_byメソッドを使うらしい

書いてる途中で非推奨であることに気づいた.ついでなので次回はfind_or_create_byについて調べてみようかな?

詳細はRailsのドキュメントを参照

参考

Ruby on Rails Documentation