すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】 createメソッドの使い方とは?new・saveメソッドとの違い

ぴっかちゃん
ぴっかちゃん

createメソッドとは、引数に渡されたデータを元にモデルのインスタンスを生成して、データベースに保存するメソッドです。

保存したいデータは、以下のコードのように引数にハッシュで渡します。

createメソッドに渡す引数のデータ -->
1
モデル名.create({ カラム名:  })

ハッシュのkeyがテーブルのカラムに紐付いており、ハッシュのvalueの値を保存します。

例えば、Userモデルにnameとageが設定されている場合は、以下のコードのようにハッシュで保存したいデータを渡すと、テーブルのカラムと紐付けて値を保存する事が出来ます。

引数のハッシュのkeyがDBのカラム名と結びつく例

実際に上記のコードを実行すると、以下のようにusersテーブルにデータを保存する事が出来ます。

Console | usersテーブルにcreateメソッドでデータを保存する
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は特に指定しなくても自動で値が入ります。

usersテーブルにデータ保存が反映される様子

createメソッドは、データベースにデータを保存する際によく使われるメソッドなので使い方をマスターしていきましょう!

createメソッドの使い方

この章では、createメソッドの基礎情報から他の似ているメソッドと比較しながら違いや使い分けについて学ぶ事が出来ます。

基本構文

createメソッドの引数には、保存したいデータをハッシュで渡します。

基本構文-->
1
モデル名.create({ カラム名:  })
createメソッドのサンプルコード-->
1
User.create({ name: "田中ゆうこ", age: 18 })

そして、ハッシュを引数で使う場合は波括弧{}を省略する事ができるため、以下のコードのように省略して簡潔に書くのが一般的です

ハッシュの波括弧を省略した場合-->
1
User.create(name: "田中ゆうこ", age: 18)

また、createメソッドの返り値は、「生成されたモデルのインスタンス」が返ります。その為、以下のコードのように変数に代入して利用する事が出来ます。

Console | 返り値のインスタンスを変数に代入して利用する
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
ポイント
  1. 保存したいデータは、ハッシュでcreateメソッドの引数に渡す
  2. ハッシュの波括弧{}は、省略して記述する事ができる
  3. createメソッドの返り値は、生成したモデルのインスタンス

saveメソッドとの違いとは?

データベースに保存するメソッドには、createメソッドの他にsaveメソッドがあります。

createメソッドは、モデルのインスタンス生成と保存を同時に行うクラスメソッドです。返り値は、以下のコードのように生成されたモデルのインスタンスが返ります。

Console | 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>
createメソッドのポイント
  1. データの保存だけではなく、モデルのインスタンス生成も同時に行う
  2. 返り値は、生成されたインスタンスが返る

一方でsaveメソッドは、インスタンスをデータベースに保存するインスタンスメソッドです。保存だけでインスタンスは生成しません。

したがって、インスタンス生成と保存を行う場合はnewメソッドとセットで使います。

Console | saveメソッドでデータを保存する場合
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を返します。

saveメソッドのポイント
  1. モデルのインスタンスをデータベースに保存するメソッド
  2. 返り値は、truefalseのbooleanを返す

複数のインスタンスを保存する方法

複数のインスタンスを生成して保存するには、以下のように配列の要素にハッシュを使って
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つのレコードのみ保存してある状態です。

usersテーブルにデータ保存が反映される様子

そこで、先ほどのコードを以下のように実際に実行してみます。

Console | createメソッドで複数のインスタンスを保存する場合-->
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メソッドなどでインスタンスを繰り返し保存する方法もあります。

Console | 配列で複数インスタンスを一度に保存する方法-->
1
2
3
# users = [{ name: "青木あお", age: 18 }, { name: "高橋なおき", age: 33 }, { name: "吉田りく", age: 22 }]

User.create(users) #複数インスタンスの生成と保存を一度に実行する
Console | eachメソッドを使って1つ1つのインスタンスを保存する場合-->
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メソッドを使うより、引数に配列を渡して一度で実行する方法を使いましょう。

ポイント

eachメソッドで複数データを1つずつ取り出してcreateを実行するよりも、複数のデータをcreateの引数に渡して一度に実行した方が処理が早い

アソシエーションで保存する方法

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のように田中さんの飼っている猫を直感的に取得する事が出来ます。

Console | アソシエーションで田中さんの飼っている猫を取得する-->
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.catscreateメソッドを使って保存する事が出来ます。

Console | 田中さんの飼い猫を保存する-->
1
2
owner.cats.create(name: 'ミケ') 
=> #<Cat:0x00007fd1aeeda620 id: 5, name: "ミケ", owner_id: 1>

もう一度owner.catsを実行すると、上記で保存したミケが追加されて田中さんの飼い猫が2匹に増えていますね。

Console | 田中さんの飼い猫を再度確認する-->
1
2
3
 owner.cats
=> [#<Cat:0x00007fd1154a3420 id: 2, name: "モモ", owner_id: 1,省略>,
#<Cat:0x00007fd1aeeda620 id: 5, name: "ミケ", owner_id: 1,省略>]

catsテーブルを見ると、以下の画像の様に田中さんの飼い猫としてミケが保存されています。

田中さんの飼い猫が2匹なことを確認する

このようにアソシエーションを使ってcreateメソッドで保存すると、簡単に関連付けされているモデルのインスタンスを生成して保存する事が出来ます。

アソシエーションを使うメリット

アソシエーションを利用しなくても、以下のコードのように外部キー制約が設定されたカラム(owner_id)の値に田中さんの主キー1を指定すれば、田中さんの飼い猫としてcatsテーブルに保存することは可能です。

Console | 外部キー制約の設定されたカラムに値を指定して保存する場合
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の値を間違えると、別の飼い主の猫として保存されてしまうからです。

一方でアソシエーションを利用すると、以下のコードのように外部キー制約のカラムの値は手動で指定しなくても対応する主キーの値が自動で入り保存されます。

Console | 外部キー制約の設定されたカラムに値を指定して保存する場合
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メソッドで作成した方が安心です。

アソシエーションで保存するメリット
  1. 外部キー制約のカラムの値は、自動で対応する主キーの値が入る
  2. コードが何のデータを作成しているか直感的に分かりやすい

create!メソッド

createメソッドとcreate!メソッドは、データベースへ保存が成功した場合はどちらもモデルのインスタンスを返しますが、保存に失敗した場合の返り値は異なります。

この挙動の違いは、Userモデルに以下のバリデーション(presence)を設定して確認します。バリデーションによってユーザーの名前が空の場合は、データベースの保存が出来ません。

app/models/user.rb | バリデーションの設定-->
1
2
3
class User < ApplicationRecord
  validates :name, presence: true #nameカラムが空の場合は保存しない
end

まずは、保存が成功した場合の各メソッドの挙動をみていきます。
以下のコードのようにcreateメソッドもcreate!メソッドも保存が成功した場合は、モデルのインスタンスが返ります。

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の例外が発生しています。

createメソッドとcreate!メソッドの保存が失敗した場合-->
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アクションの中に記述される事が多いです。

app/controllers/users_controller.rb | -->
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メソッドを使って以下のコードのように記述する事が出来ます。

app/controllers/users_controller.rb | @userのインスタンスを保存後に使いたい場合-->
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メソッドの返り値はtruefalseなので、上記のコードのようにif文で保存が成功した時と失敗した時の処理を分ける事が出来ます。保存が成功するとredirect_toメソッド@userを渡して、ユーザーの詳細ページにリダイレクトします。

このように、インスタンスを使って何か操作したい場合は、new/saveメソッドを使います。

ポイント

new/saveメソッドは、保存前や保存後にインスタンスを使って何か操作したい場合に使いましょう!

createメソッドの場合

createメソッドは、生成したインスタンスを操作する必要がなくインスタンス生成と保存を同時に行っても良い場合に使用します。

特にインスタンスでの操作が無い場合は、以下のコードのようにcreateメソッドの方が簡潔に記述する事が出来ます。

new/saveメソッドとcreateメソッドのコード量の比較-->
1
2
3
4
5
user = User.new(name: "田中ゆうこ", age: 18) # インスタンス生成
user.save # 保存

# 以下の1行で済む
User.create(name: "田中ゆうこ", age: 18) # インスタンス生成と保存

しかし、if文を使ってデータベースの保存成功と失敗の処理を分けている場合は注意が必要です。

例えば、以下のコードはcreateメソッドにname: nilを渡しているので、検証に引っかかってデータベースへの保存が失敗します。

createメソッドでデータベース保存成功と失敗の処理を分けた場合-->
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の処理が実行されるからです。

ポイント
  1. データベースに保存が失敗しても、生成されたモデルのインスタンスが返る
  2. if文を使って処理を分けても、インスタンスが返るので失敗したときの処理が実行されない
保存失敗した際のエラーメッセージの注意点

データベースへ保存失敗した際の処理が正常に実行できるように、valid?メソッドを使えば良いのではないかと考える方もいるかもしれません。

app/controllers/users_controller.rb | createメソッドに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メソッドを使います。

app/controllers/users_controller.rb | バリデーションのエラーメッセージを表示出来るようにする-->
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メソッドを使うこと