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 | 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 | age | |
---|---|---|---|
1 | 藤井隆 | fujii@example.com | 25 |
2 | 山田太郎 | yamada@example.com | 20 |
3 | 山本健太 | yamamoto@example.com | 20 |
4 | 加藤拓哉 | kato@example.com | 30 |
となるはずである.
実行例(応用編)
データベースは先ほどと同様に以下のとおりとする.
id | name | 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のドキュメントを参照