【Rails】オブジェクトを作成したり検索したり更新したり保存したり
Ruby on Railsではデータベースとの様々なやりとりをActice Record
という仕組みを使って行いますが、よく使われる方法をざっとご紹介です。
今回利用するテーブル情報はこちらです。
Table "public.hoges" Column | Type | Collation | Nullable | Default ------------+-----------------------------+-----------+----------+----------------------------------- id | bigint | | not null | nextval('hoges_id_seq'::regclass) name | character varying | | | age | integer | | | gender | character varying | | | birthday | date | | | email | character varying | | | created_at | timestamp without time zone | | not null | updated_at | timestamp without time zone | | not null | Indexes: "hoges_pkey" PRIMARY KEY, btree (id)
オブジェクトを作成する
オブジェクトを作成するときは、new
とbuild
を使います。
> Hoge.new => #<Hoge:0x00007fe9b5bbce10 id: nil, name: nil, age: nil, gender: nil, birthday: nil, email: nil, created_at: nil, updated_at: nil>
# hogeに紐づく記事データを作成する(1 : Nの関係) def new hoge = Hoge.find params[:id] @hoge = hoge.articles.build(strong_parameter) end # hogeに紐づく家族構成データを作成する(1 : 1の関係) def new hoge = Hoge.find params[:id] @hoge = hoge.build_family_structure(strong_parameter) end
new
は単体のモデルを生成する時、build
はテーブルの関係が1 : N(has_many)
の場面で使います。
ログインが前提になるようなサービスだとnew
を使う場面は、あんまり無いと思います。
build_モデル名
はテーブルの関係が1 : 1(has_one)
の場面で利用します。
【20191002追記】
より詳細な情報・書き方まとめてみました!
オブジェクトを検索する
オブジェクトを検索する = SQLで言うwhere
文を実行する処理です。
- find
- find_by
- find_by!
- first
- last
- where
# idが1のものを1件取得 > Hoge.find 1 Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]] => #<Hoge:0x00007fe9b645f4a0 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> # nameが'opiyo'のものを1件取得 > Hoge.find_by(name: 'opiyo') Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] => #<Hoge:0x00007fe9d4d21e30 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> # 一番最初に登録されたものを取得 > Hoge.first Hoge Load (0.3ms) SELECT "hoges".* FROM "hoges" ORDER BY "hoges"."id" ASC LIMIT $1 [["LIMIT", 1]] => #<Hoge:0x00007fe9b6242e60 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> # 一番最後に登録されたものを取得 > Hoge.last Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" ORDER BY "hoges"."id" DESC LIMIT $1 [["LIMIT", 1]] => #<Hoge:0x00007fe9b61b97c8 id: 2, name: "tako", age: 20, gender: nil, birthday: Sat, 23 Oct 1999, email: nil, created_at: Tue, 01 Oct 2019 21:50:26 JST +09:00, updated_at: Tue, 01 Oct 2019 21:50:26 JST +09:00> # 条件に一致するものを複数件取得 > Hoge.where(gender: nil) Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."gender" IS NULL => [#<Hoge:0x00007fe9b59b6ff8 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00>, #<Hoge:0x00007fe9b59b6e40 id: 2, name: "tako", age: 20, gender: nil, birthday: Sat, 23 Oct 1999, email: nil, created_at: Tue, 01 Oct 2019 21:50:26 JST +09:00, updated_at: Tue, 01 Oct 2019 21:50:26 JST +09:00>] # データが見つからなかった時 > Hoge.find_by!(name: 'opi') Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opi"], ["LIMIT", 1]] ActiveRecord::RecordNotFound: Couldn't find Hoge
少しややこしいのが、find_by
とwhere
です。find_by
は必ず1件のデータのみ返却しますので1件を保証される場合はこちらを使います。
また、find_by!
にすることで1件も見つからない場合は例外を発生させることもあります。
join
やgroup
、order
なども出来ますので、こちらはまた別の記事でまとめてみたいと思います。
【20191007追記】
より詳細な情報・書き方まとめてみました!
オブジェクトの保存・更新
オブジェクトの保存・更新は種類がいっぱいあります。
- save
- save!
- update
- update!
- update_attribute
- update_attributes
- update_attributes!
- update_column
- update_columns
- update_all
全てデータの保存や更新する処理になりますが、違いのポイントとしては以下があります。
- 戻り値の違い「true/false」 or 「例外」
- コールバックアクション(before_action/after_action)の有無
- バリデーションの有無
!
がついた場合は例外を返します。
update_column
はコールバックやバリデーションを無視しますので、ajaxやrakeタスクなど非同期で処理している時に使うことが多いと思います。が、基本的にはupdate_attributes
を使います。
【20191007追記】
より詳細な情報・書き方まとめてみました!
オブジェクトの削除
削除する処理はdestroy
とdelete
の二つになります。
- destroy
- delete
- destroy_all
- delete_all
大きな違いは、destroy
の場合は関連するデータも削除しますがdelete
の場合はそのデータのみを削除します。
【20191003追記】
より詳細な情報・書き方まとめてみました!
合わせて一本!
探して作って、探して保存してを実現する便利なメソッドもあるのでそれもご紹介です!
- find_or_created_by!
- find_or_initialize_by
# findして、あればデータを無ければnew > Hoge.find_or_initialize_by(name: 'opiyo') Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] => #<Hoge:0x00007fe9d4d57418 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> > Hoge.find_or_initialize_by(name: 'opiy') Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiy"], ["LIMIT", 1]] => #<Hoge:0x00007fe9d4c692b8 id: nil, name: "opiy", age: nil, gender: nil, birthday: nil, email: nil, created_at: nil, updated_at: nil> # findして、あればデータを無ければcreate > Hoge.find_or_create_by!(name: 'opiyo', age: 30) Hoge Load (0.3ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 AND "hoges"."age" = $2 LIMIT $3 [["name", "opiyo"], ["age", 30], ["LIMIT", 1]] => #<Hoge:0x00007fe9b6190788 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> > Hoge.find_or_create_by!(name: 'opiy', age: 40) Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 AND "hoges"."age" = $2 LIMIT $3 [["name", "opiy"], ["age", 40], ["LIMIT", 1]] (0.2ms) BEGIN Hoge Create (0.3ms) INSERT INTO "hoges" ("name", "age", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "opiy"], ["age", 40], ["created_at", "2019-10-01 13:27:21.287452"], ["updated_at", "2019-10-01 13:27:21.287452"]] (4.0ms) COMMIT => #<Hoge:0x00007fe9d17bc358 id: 3, name: "opiy", age: 40, gender: nil, birthday: nil, email: nil, created_at: Tue, 01 Oct 2019 22:27:21 JST +09:00, updated_at: Tue, 01 Oct 2019 22:27:21 JST +09:00>
これ以外にも基本的に上記で紹介した処理は繋げて書くことが可能です。
> Hoge.find_by(name: 'opiyo') Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] => #<Hoge:0x00007fe9d4811508 id: 1, name: "opiyo", age: 30, gender: nil, birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Mon, 30 Sep 2019 20:47:43 JST +09:00> > Hoge.find_by(name: 'opiyo').update_attributes(gender: '男性') Hoge Load (0.4ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] (0.2ms) BEGIN Hoge Update (0.5ms) UPDATE "hoges" SET "gender" = $1, "updated_at" = $2 WHERE "hoges"."id" = $3 [["gender", "\xE7\x94\xB7\xE6\x80\xA7"], ["updated_at", "2019-10-01 13:29:00.694154"], ["id", 1]] (0.7ms) COMMIT => true > Hoge.find_by(name: 'opiyo') Hoge Load (0.6ms) SELECT "hoges".* FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] => #<Hoge:0x00007fe9b8121070 id: 1, name: "opiyo", age: 30, gender: "男性", birthday: Fri, 04 Aug 1989, email: nil, created_at: Mon, 30 Sep 2019 20:47:43 JST +09:00, updated_at: Tue, 01 Oct 2019 22:29:00 JST +09:00>
ざっとよく使われるものを紹介してきましたが、どんな場面でどのように使うのか。こんな時はどうするのかなど一杯あると思いますのでまた別の機会で、 深掘りした内容もまとめていきたいと思います。