更新日:
【Rails】 createメソッドの使い方とは?new・saveメソッドとの違い
createメソッドとは、引数に渡されたデータを元にモデルのインスタンスを生成して、データベースに保存するメソッドです。
保存したいデータは、以下のコードのように引数にハッシュで渡します。
1
モデル名.create({ カラム名: 値 })
ハッシュのkeyがテーブルのカラムに紐付いており、ハッシュのvalueの値を保存します。
例えば、Userモデルにnameとageが設定されている場合は、以下のコードのようにハッシュで保存したいデータを渡すと、テーブルのカラムと紐付けて値を保存する事が出来ます。
実際に上記のコードを実行すると、以下のようにusersテーブルにデータを保存する事が出来ます。
1
2
3
4
5
6
7
8
9
[1] pry(main)> User.create({ name: "田中ゆうこ", age: 18 })
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('田中ゆうこ', 18, '2020-09-17 15:37:13', '2020-09-17 15:37:13')
=> #<User:0x00007fd1ae954390
id: 1,
name: "田中ゆうこ",
age: 18,
created_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00,
updated_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00
>
id
,created_at
,updated_at
は特に指定しなくても自動で値が入ります。
createメソッドは、データベースにデータを保存する際によく使われるメソッドなので使い方をマスターしていきましょう!
createメソッドの使い方
この章では、createメソッドの基礎情報から他の似ているメソッドと比較しながら違いや使い分けについて学ぶ事が出来ます。
基本構文
createメソッドの引数には、保存したいデータをハッシュで渡します。
1
モデル名.create({ カラム名: 値 })
1
User.create({ name: "田中ゆうこ", age: 18 })
そして、ハッシュを引数で使う場合は波括弧{}
を省略する事ができるため、以下のコードのように省略して簡潔に書くのが一般的です
1
User.create(name: "田中ゆうこ", age: 18)
また、createメソッドの返り値は、「生成されたモデルのインスタンス」が返ります。その為、以下のコードのように変数に代入して利用する事が出来ます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[1] pry(main)> user = User.create(name: "田中ゆうこ", age: 18)
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('田中ゆうこ', 18, '2020-09-17 15:37:13', '2020-09-17 15:37:13')
=> #<User:0x00007fd1ae954390 # 返り値はモデルのインスタンス
id: 1,
name: "田中ゆうこ",
age: 18,
created_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00,
updated_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00
>
[2] pry(main)> user.name
=> "田中ゆうこ"
[3] pry(main)> user.age
=> 18
saveメソッドとの違いとは?
データベースに保存するメソッドには、createメソッドの他にsaveメソッドがあります。
createメソッドは、モデルのインスタンス生成と保存を同時に行うクラスメソッドです。返り値は、以下のコードのように生成されたモデルのインスタンスが返ります。
1
2
3
4
5
[1] pry(main)> User.create(name: "田中ゆうこ", age: 18) #インスタンス生成と保存をする
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('田中ゆうこ', 18, '2020-09-17 15:37:13', '2020-09-17 15:37:13')
# 返り値は生成されたモデルのインスタンス
=> #<User id: 1, name: "田中ゆうこ", age: 18, created_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00, updated_at: Thu, 17 Sep 2020 15:37:13 UTC +00:00>
一方でsaveメソッドは、インスタンスをデータベースに保存するインスタンスメソッドです。保存だけでインスタンスは生成しません。
したがって、インスタンス生成と保存を行う場合はnewメソッドとセットで使います。
1
2
3
4
5
6
7
[1] pry(main)> user = User.new(name: "田中ゆうこ", age: 18) # インスタンス生成
=> #<User id: nil, name: "田中ゆうこ", age: 18, created_at: nil, updated_at: nil>
[1] pry(main)> user.save # インスタンスをデータベースへ保存
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('田中ゆうこ', 18, '2020-09-17 15:37:13', '2020-09-17 15:37:13')
=> true #返り値はboolean
createメソッドの場合は、createメソッドだけを実行して保存できるのに対して、saveメソッドの場合は、newメソッドとセットでデータベースに保存できます。つまりcreateメソッドは、newとsaveを同時に行っているメソッドだと分かりますね。
ただし、返り値はboolean
になるので注意して下さい。検証が失敗した場合にfalse
を返します。
複数のインスタンスを保存する方法
複数のインスタンスを生成して保存するには、以下のように配列の要素にハッシュを使って
1つ1つ保存したいデータを記述します。
1
モデル名.create([{ データ1 }, { データ2 }, { データ3 }])
例えば、Userモデルにnameとageが設定されている場合は、以下のコードのように配列の要素にハッシュで保存したい複数のデータを引数に渡すと保存する事が出来ます。
1
2
3
4
5
User.create([
{ name: "青木あお", age: 18 },
{ name: "高橋なおき", age: 33 },
{ name: "吉田りく", age: 22 }
])
usersテーブルは、以下の画像のように現在は1つのレコードのみ保存してある状態です。
そこで、先ほどのコードを以下のように実際に実行してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
User.create([
{ name: "青木あお", age: 18 },
{ name: "高橋なおき", age: 33 },
{ name: "吉田りく", age: 22 }
])
# 発行されるSQL
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('青木あお', 18, '2020-09-17 17:39:53', '2020-09-17 17:39:53')
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('高橋なおき', 33, '2020-09-17 17:39:53', '2020-09-17 17:39:53')
INSERT INTO `users` (`name`, `age`, `created_at`, `updated_at`) VALUES ('吉田りく', 22, '2020-09-17 17:39:53', '2020-09-17 17:39:53')
# 返り値
=> [#<User:0x00007fd1123fd910
id: 2,
name: "青木あお",
age: 18>,
#<User:0x00007fd1155d4588
id: 3,
name: "高橋なおき",
age: 33>,
#<User:0x00007fd1155ce250
id: 4,
name: "吉田りく",
age: 22>] # createdとupdatedは省略してます
返り値は、引数に渡した3つデータのインスタンスが生成されています。そして、以下の画像のようにusersテーブルにもしっかり保存もされていますね。
配列で複数のデータを引数に渡すメリットとは?
複数のインスタンスを保存するには、createメソッドの引数に複数のインスタンスを配列で渡す以外にも、以下のコードのようにeachメソッドなどでインスタンスを繰り返し保存する方法もあります。
1
2
3
# users = [{ name: "青木あお", age: 18 }, { name: "高橋なおき", age: 33 }, { name: "吉田りく", age: 22 }]
User.create(users) #複数インスタンスの生成と保存を一度に実行する
1
2
3
4
5
# users = [{ name: "青木あお", age: 18 }, { name: "高橋なおき", age: 33 }, { name: "吉田りく", age: 22 }]
users.each do |user| #usersから1つ1つデータを取り出し、userに渡す
User.create(user) #インスタンス生成と保存を実行
end # usersの中身が無くなるまで繰り返す
しかし、eachメソッドを使って1つ1つ保存するよりも、配列で複数インスタンスを保存した方が処理が早いというメリットがあります。
以下の画像の枠の部分は、実際にデータベースに保存されるまで掛かった時間です。
eachメソッドの方が処理の時間が遅いことが分かりますね。
その為、複数のデータをcreateメソッドで保存する場合はeachメソッドを使うより、引数に配列を渡して一度で実行する方法を使いましょう。
アソシエーションで保存する方法
createメソッドは、アソシエーションを使ってインスタンスの生成と保存をする事が出来ます。この章では、以下のようにアソシエーションが定義されたOwnerモデルとCatモデルを使って解説していきます。
1
2
3
4
5
6
7
8
9
# app/models/owner.rb
class Owner < ApplicationRecord
has_many :cats
end
# app/models/cat.rb
class Cat < ApplicationRecord
belongs_to :owner
end
上記のアソシエーションは、「1人の飼い主は沢山の猫を飼っているが、猫は1人の飼い主に属す(Owner has many Cats)」という関係を表します。
OwnerモデルとCatモデルに対応するownersテーブルとcatsテーブルの中身は、以下の通りです。田中さん(id=1)がモモという名前の猫を飼っている事が分かりますね。
アソシエーションを定義すると、以下のowner.cats
のように田中さんの飼っている猫を直感的に取得する事が出来ます。
1
2
3
4
5
owner = Owner.first
=> #<Owner:0x00007fd1123d5b18 id: 1, name: "田中", age: 23,省略>
owner.cats # 田中さんの飼い猫を取得する
=> [#<Cat:0x00007fd1154a3420 id: 2, name: "モモ", owner_id: 1,省略>]
そして、田中さんの飼っている猫をもう1匹増やしたい場合は、以下のコードのようにowner.cats
にcreateメソッドを使って保存する事が出来ます。
1
2
owner.cats.create(name: 'ミケ')
=> #<Cat:0x00007fd1aeeda620 id: 5, name: "ミケ", owner_id: 1>
もう一度owner.cats
を実行すると、上記で保存したミケが追加されて田中さんの飼い猫が2匹に増えていますね。
1
2
3
owner.cats
=> [#<Cat:0x00007fd1154a3420 id: 2, name: "モモ", owner_id: 1,省略>,
#<Cat:0x00007fd1aeeda620 id: 5, name: "ミケ", owner_id: 1,省略>]
catsテーブルを見ると、以下の画像の様に田中さんの飼い猫としてミケが保存されています。
このようにアソシエーションを使ってcreateメソッドで保存すると、簡単に関連付けされているモデルのインスタンスを生成して保存する事が出来ます。
アソシエーションを使うメリット
アソシエーションを利用しなくても、以下のコードのように外部キー制約が設定されたカラム(owner_id
)の値に田中さんの主キー1
を指定すれば、田中さんの飼い猫としてcatsテーブルに保存することは可能です。
1
2
3
4
5
6
7
8
9
[[1] pry(main)> Cat.create(name: 'ココ', owner_id: 1)
SELECT `owners`.* FROM `owners` WHERE `owners`.`id` = 1 LIMIT 1
INSERT INTO `cats` (`name`, `created_at`, `updated_at`, `owner_id`) VALUES ('ココ', '2020-10-02 03:48:42', '2020-10-02 03:48:42', 1)
=> #<Cat:0x00007ffd3cf48cd8
id: 7,
name: "ココ",
created_at: Fri, 02 Oct 2020 03:48:42 UTC +00:00,
updated_at: Fri, 02 Oct 2020 03:48:42 UTC +00:00,
owner_id: 1>
しかし、上記のコードだと外部キー制約が設定されたカラムの値を手動で指定しなければなりません。これはデータを管理する上で安全とは言えません。もし、何かの手違いでowner_id
の値を間違えると、別の飼い主の猫として保存されてしまうからです。
一方でアソシエーションを利用すると、以下のコードのように外部キー制約のカラムの値は手動で指定しなくても対応する主キーの値が自動で入り保存されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[2] pry(main)> owner = Owner.find_by(name: '田中')
SELECT `owners`.* FROM `owners` WHERE `owners`.`name` = '田中' LIMIT 1
=> #<Owner:0x00007ffd40d37698
id: 1,
name: "田中",
created_at: Wed, 25 Dec 2019 02:43:01 UTC +00:00,
updated_at: Wed, 25 Dec 2019 02:43:01 UTC +00:00,
age: 23>
# owner_idの値を手動で指定する必要がない
[3] pry(main)> owner.cats.create(name: 'ココ')
INSERT INTO `cats` (`name`, `created_at`, `updated_at`, `owner_id`) VALUES ('ココ', '2020-10-02 04:01:14', '2020-10-02 04:01:14', 1)
=> #<Cat:0x00007ffd40b27c68
id: 7,
name: "ココ",
created_at: Fri, 02 Oct 2020 04:01:14 UTC +00:00,
updated_at: Fri, 02 Oct 2020 04:01:14 UTC +00:00,
owner_id: 1>
その為、owner_id
の値を間違えてデータを作成してしまうミスを防ぐ事ができます。また、以下のコードのようにアソシエーションを利用したコードの方が誰の飼い猫を作成しているか直感的に分かります。
1
2
3
4
5
6
# アソシエーションを利用しない場合
Cat.create(name: 'ココ', owner_id: 1)
# アソシエーションを利用した場合
owner = Owner.find_by(name: '田中')
owner.cats.create(name: 'ココ')
このように親アソシエーションがあるレコードを作成する場合は、何か間違いを起こさないためにも親アソシエーションのインスタンスからcreateメソッドで作成した方が安心です。
create!メソッド
createメソッドとcreate!メソッドは、データベースへ保存が成功した場合はどちらもモデルのインスタンスを返しますが、保存に失敗した場合の返り値は異なります。
この挙動の違いは、Userモデルに以下のバリデーション(presence)を設定して確認します。バリデーションによってユーザーの名前が空の場合は、データベースの保存が出来ません。
1
2
3
class User < ApplicationRecord
validates :name, presence: true #nameカラムが空の場合は保存しない
end
まずは、保存が成功した場合の各メソッドの挙動をみていきます。
以下のコードのようにcreateメソッドもcreate!メソッドも保存が成功した場合は、モデルのインスタンスが返ります。
1
2
3
4
5
User.create(name: '林田 紀子', age: 40)
=> #<User:0x00007ffb4bd7ade0 id: 5, name: "林田 紀子", age: 40>
User.create!(name: '林田 紀子', age: 40)
=> #<User:0x00007ffb4bd7ade0 id: 5, name: "林田 紀子", age: 40>
しかし、name: nil
にして保存を失敗させると、以下のコードのようにcreateはモデルのインスタンスが返るのに対して、create!の方はActiveRecord::RecordInvalid
の例外が発生しています。
1
2
3
4
5
User.create(name: nil, age:20)
=> #<User:0x00007ffb4bbe13d0 id: nil, name: nil, age: 20>
User.create!(name: nil, age:20)
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
複数のデータ保存を順次行うときに、トランザクションという機能を使うことがあります。その場合はcreate!を使用すると、例外の発生とともにロールバック処理が行われるのでcreateより便利です。
createメソッドはコントローラーのどこに書くか
createメソッドは、コントローラーの7つのアクションのうち、createアクションの中に記述される事が多いです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.create(user_params)
end
private
def user_params
params.require(:user).permit(:name, :age)
end
end
createアクションは、通常newアクションとセットで使用します。
ユーザーの新規作成ページを表示するnewアクションでフォームから送信されるフォーム情報をcreateアクションで受け取り、createメソッドを使ってユーザー情報を保存します。
アクション名 | 機能 |
---|---|
index | リソースの一覧を表示させる |
show | リソースの詳細を表示させる |
new | 投稿フォームを表示させる |
create | リソースを追加させる |
edit | 更新フォームを表示させる |
update | リソースを更新させる |
destroy | リソースを削除する |
Railsではこの7つのアクションに従ってメソッドを記述することが、可読性を高める上で重要です。使いわけに自信がない場合は、こちらの「resourcesメソッドを徹底解説!」の記事もご参照ください。
new/saveメソッドとcreateメソッドの使い分け
new/saveメソッドの場合
new/saveメソッドの使いどころは、生成したインスタンスを使って何かしらの操作を行いたい場合です。
例えばデータベースの保存が成功したら、そのインスタンスの詳細ページへリダイレクトさせたい場合などは、new/saveメソッドを使って以下のコードのように記述する事が出来ます。
1
2
3
4
5
6
7
8
9
def create
@user = User.new(user_params) # インスタンス生成
if @user.save
# 保存が成功した際の処理
redirect_to @user, notice: 'User was successfully created.'
else
render :new # 失敗した際の処理
end
end
saveメソッドの返り値はtrue
かfalse
なので、上記のコードのようにif文で保存が成功した時と失敗した時の処理を分ける事が出来ます。保存が成功するとredirect_toメソッドに@user
を渡して、ユーザーの詳細ページにリダイレクトします。
このように、インスタンスを使って何か操作したい場合は、new/saveメソッドを使います。
createメソッドの場合
createメソッドは、生成したインスタンスを操作する必要がなくインスタンス生成と保存を同時に行っても良い場合に使用します。
特にインスタンスでの操作が無い場合は、以下のコードのようにcreateメソッドの方が簡潔に記述する事が出来ます。
1
2
3
4
5
user = User.new(name: "田中ゆうこ", age: 18) # インスタンス生成
user.save # 保存
# 以下の1行で済む
User.create(name: "田中ゆうこ", age: 18) # インスタンス生成と保存
しかし、if文を使ってデータベースの保存成功と失敗の処理を分けている場合は注意が必要です。
例えば、以下のコードはcreateメソッドにname: nil
を渡しているので、検証に引っかかってデータベースへの保存が失敗します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# user.rb
class User < ApplicationRecord
validates :name, presence: true #nameカラムが空の場合は保存しない
end
# users_controller.rb
class UsersController < ApplicationController
def create
if User.create(name: nil, age: 18) # 保存は失敗する
redirect_to root_path # 保存成功に実行したい処理
else
render 'new' # 保存失敗に実行したい処理
end
end
データベースへの保存が失敗したので、保存失敗の処理に記述するrender 'new'
を実行したいところですが、なぜか保存成功の処理が実行されルートにリダイレクトされます。
これは、createメソッドの返り値がデータベースの保存に失敗してもモデルのインスタンスを返す ので、if文のtrueの処理が実行されるからです。
保存失敗した際のエラーメッセージの注意点
データベースへ保存失敗した際の処理が正常に実行できるように、valid?メソッドを使えば良いのではないかと考える方もいるかもしれません。
1
2
3
4
5
6
7
8
9
10
11
class UsersController < ApplicationController
def create
#valid?メソッドで保存できる状態か確認する, 返り値はbooleanが返る
if User.create(name: nil, age: 18).valid?
# 保存成功の処理
redirect_to root_path
else
# 保存失敗の処理
render ‘new’
end
end
しかし、上記のコードではインスタンス変数を作っていないので、保存する事が出来なくてエラーが出た場合に、インスタンス変数にエラーメッセージを格納してnewページにレンダリングする事が出来ません。
その為、基本的にバリデーションのエラーメッセージをnewで表示する場合は、以下のようにnew/saveメソッドを使います。
1
2
3
4
5
6
7
8
def create
@user = User.new(user_params) # インスタンス生成
if @user.save # 保存が成功した際の処理
redirect_to @user, notice: 'User was successfully created.'
else # 失敗した際の処理
render :new
end
end
上記のコードは、保存に成功した場合に@user
を使って詳細ページにリダイレクトしていますが、失敗した場合も@user
に格納されたエラーメッセージをnewで表示出来るようにしているのです。
似ているメソッドまとめ
これまでに解説した各メソッドの特徴は、以下の表の通りです。
メソッド | 種類 | 返り値 | 用途 |
---|---|---|---|
save | インスタンスメソッド | boolean | インスタンスを使って何か操作したい場合 |
create | クラスメソッド | モデルのインスタンス | インスタンス生成と保存を同時にしても良い場合 |
create! | クラスメソッド | 保存失敗時は例外を発生 | 複数のデータ保存を行う場合 |
使用場面や用途によってメソッドを使い分けれるようになりましょう!
この記事のまとめ
- createメソッドとは、引数に渡されたデータを元にモデルのインスタンスを生成して、データベースに保存するクラスメソッド
- 返り値は、生成されたモデルのインスタンス
- バリデーションのエラーメッセージを表示する場合は、createメソッドではなくnew/saveメソッドを使うこと