Railsのモデルを使うと、データベースに直接アクセスせずに、Rails経由でデータベースの操作を行うことができます。
データベースがMySQLやPostgreSQLに関わらず同じコマンドでデータベースを操作できるのでとても簡単です。
Railsの対話モードに入る
Railsからテーブル操作を行うには、対話モードに入る必要があります。
rails c
コマンドを実行します。(rails console の略です。どちらのコマンドでも対話モードに入れます)
# rails c
Running via Spring preloader in process 197
Loading development environment (Rails 6.1.4)
irb(main):001:0>
Docker上のRailsの場合
Docker上のRailsを使っている場合は、Railsコンテナに入ってから、対話モードに入る必要があります。
Railsコンテナが起動中である必要があります。
#起動中のコンテナ一覧を確認
docker ps
#Railsコンテナに入る
$ docker exec -it <コンテナ名> bash
#対話モードに入る
root@d90f784d2e64:/rails-vue# rails c
Running via Spring preloader in process 229
Loading development environment (Rails 6.1.4)
irb(main):001:0>
モデル経由でのテーブル操作の注意点
モデル経由でテーブルを操作できるのは、テーブルや中身の表示、一部のデータの追加・変更・削除です。
テーブルにカラムを追加したり、カラムを削除するといった、スキーマ自体の変更はマイグレーションファイル経由で行う必要があります。
また、スキーマの変更内容をRailsの対話モードに反映する場合は、一度コンテナから出て、再度コンテナに入り直す必要があります。
#対話モードから抜ける
irb(main):004:0> exit
#コンテナから抜ける
root@a3d49a8a5adf:/rails-vue# exit
exit
#コンテナに入る
docker exec -it <コンテナ名> bash
カラムを追加するマイグレーションファイルの作成方法
次のコマンドを実行すると、カラムを追加するためのマイグレーションファイルを生成してくれます。
rails g migration Add<カラム名>To<モデル名> <カラム名1:型1> <カラム名2:型2>,,,
カラムを削除するマイグレーションファイルの作成方法
次のコマンドを実行すると、カラムを追加するためのマイグレーションファイルを生成してくれます。
rails g migration Remove<カラム名>To<モデル名> <カラム名1:型1> <カラム名2:型2>,,,
他にもカラムを追加したり、ユニークの設定をつける場合は、生成したマイグレーションファイルを直接編集します。
編集後に、rails db:migrate
を実行します。
スキーマを変更した後は、 Railsの対話モードに反映する場合は、一度コンテナから出て再度コンテナに入り直す必要があります。
テーブル一覧の表示
テーブルの一覧を表示するには、ActiveRecord::Base.connection.tables
を実行します
irb(main):001:0> ActiveRecord::Base.connection.tables
=> ["schema_migrations", "ar_internal_metadata", "clients", "active_admin_comments", "tests"]
上記の例だと、次の5つのテーブルが存在していることがわかります。
- schema_migrations
- ar_internal_metadata
- clients
- active_admin_comments
- tests
テーブルのカラム一覧を表示する
テーブルの中にどんなカラムが入っているかを確認したい場合は次のコードを実行します。
検索結果は配列です。
モデル名.column_names
実例
#テーブル一覧を表示
irb(main):001:0> ActiveRecord::Base.connection.tables
=> ["schema_migrations", "ar_internal_metadata", "clients", "active_admin_comments", "tests"]
#clientsテーブルのカラム一覧を表示
irb(main):003:0> Client.column_names
=> ["id", "pj_name", "client_name", "status", "order_date", "price", "memo", "created_at", "updated_at"]
clientsテーブルのカラム一覧を確認したいので、モデル名はClientになります。
テーブルの中身を確認する
テーブルの中身を確認する方法は大きく3つあります。
- すべて表示(all)
- id番号で指定(find)
- カラムとデータを指定(find_by)
- 番号をアルファベットで指定 (first,,,fifth, last)
- 条件で絞り込み(where)
すべて表示(all)
テーブルの中の全てのデータを見たい場合はallメソッドを使います。検索結果はActiveRecord::Relationのインスタンスです。
モデル名.all
実例
#データが空の場合
irb(main):031:0> Test.all
Test Load (0.6ms) SELECT "tests".* FROM "tests" /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation []>
#データが複数存在する場合
irb(main):032:0> Test.all
Test Load (0.7ms) SELECT "tests".* FROM "tests" /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 9, name: "ユーザー2", created_at: "2021-07-20 00:30:31.939292000 +0000", updated_at: "2021-07-20 00:30:31.939292000 +0000">, #<Test id: 10, name: "ユーザー3", created_at: "2021-07-20 00:30:42.764326000 +0000", updated_at: "2021-07-20 00:30:42.764326000 +0000">]>
id番号で指定(find)
id番号で指定したデータを取得することもできます。
モデル名.find(id番号)
id番号は複数指定することもできます。
モデル名.find(id1, id2, id3,,,)
実例
#id番号9を検索
irb(main):035:0> Test.find(9)
Test Load (0.5ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 LIMIT $2 [["id", 9], ["LIMIT", 1]]
=> #<Test id: 9, name: "ユーザー2", created_at: "2021-07-20 00:30:31.939292000 +0000", updated_at: "2021-07-20 00:30:31.939292000 +0000">
#複数検索 8,9,10
irb(main):036:0> Test.find(8,9,10)
Test Load (0.9ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3) [["id", 8], ["id", 9], ["id", 10]]
=> [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 9, name: "ユーザー2", created_at: "2021-07-20 00:30:31.939292000 +0000", updated_at: "2021-07-20 00:30:31.939292000 +0000">, #<Test id: 10, name: "ユーザー3", created_at: "2021-07-20 00:30:42.764326000 +0000", updated_at: "2021-07-20 00:30:42.764326000 +0000">]
▼指定したIDが存在しない場合
#idが存在しない場合
irb(main):037:0> Test.find(1)
Test Load (0.8ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):37
ActiveRecord::RecordNotFound (Couldn't find Test with 'id'=1)
#複数指定のうち1つが存在しない場合
irb(main):040:0> Test.find(8, 9, 1)
Test Load (0.8ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3) [["id", 8], ["id", 9], ["id", 1]]
Traceback (most recent call last):
1: from (irb):40
ActiveRecord::RecordNotFound (Couldn't find all Tests with 'id': (8, 9, 1) (found 2 results, but was looking for 3).)
カラムとデータを指定(find_by)
検索対象となるカラムとデータを指定して検索することもできます。(※データの1つがわかっている場合に使えます)
モデル名.find_by(カラム名:データ)
実例
#nameカラムがユーザー1のデータを検索
irb(main):063:0> Test.find_by(name:"ユーザー1")
Test Load (0.9ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 LIMIT $2 [["name", "ユーザー1"], ["LIMIT", 1]]
=> #<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">
#データが存在しない場合
irb(main):064:0> Test.find_by(name:"ユーザー")
Test Load (0.7ms) SELECT "tests".* FROM "tests" WHERE "tests"."name" = $1 LIMIT $2 [["name", "ユーザー"], ["LIMIT", 1]]
=> nil
番号をアルファベットで指定 (first,,,fifth, last)
対象となるテーブルの中で何番目のデータが欲しいかを指定することもできます。指定は英語(序数)で行います。
※指定は1~5と、一番最後のみです(なぜか42 forty_two のみ指定できます。)
モデル名.英語表記の番号
実例
#first
irb(main):017:0> Test.first
Test Load (0.5ms) SELECT "tests".* FROM "tests" ORDER BY "tests"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">
#thirtd
irb(main):018:0> Test.third
Test Load (0.7ms) SELECT "tests".* FROM "tests" ORDER BY "tests"."id" ASC LIMIT $1 OFFSET $2 [["LIMIT", 1], ["OFFSET", 2]]
=> #<Test id: 10, name: "ユーザー3", created_at: "2021-07-20 00:30:42.764326000 +0000", updated_at: "2021-07-20 00:30:42.764326000 +0000">
#last
irb(main):020:0> Test.last
Test Load (0.5ms) SELECT "tests".* FROM "tests" ORDER BY "tests"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Test id: 18, name: "ユーザー11", created_at: "2021-07-20 02:14:55.144603000 +0000", updated_at: "2021-07-20 02:14:55.144603000 +0000">
▼存在しない場合
- 対象のデータが存在しない場合はnilが返ります。
- メソッドが存在しない場合はエラーになります。(sixthやtenthなど)
#データが存在しない場合
irb(main):019:0> Test.forty_two
Test Load (0.6ms) SELECT "tests".* FROM "tests" ORDER BY "tests"."id" ASC LIMIT $1 OFFSET $2 [["LIMIT", 1], ["OFFSET", 41]]
=> nil
#メソッドがない場合
irb(main):021:0> Test.sixth
Traceback (most recent call last):
1: from (irb):21
NoMethodError (undefined method `sixth' for #<Class:0x000055e8c803b018>)
条件で絞り込み(where)
最も実用的な検索方法は、whereを使って様々な条件の絞り込みを行うことです。
モデル名.where(条件)
条件の指定方法は複数あります。
- カラム名と値を指定
(カラム名: 値)
- 複数の条件を指定
(カラム名: [値1, 値2,,,])
- 以上、以下の指定
("カラム名 > ?", 数値)
、("カラム名 <= ?", 数値)
- かつ(AND)の指定
where(条件).where(条件)
- かつ(AND)の指定
("カラム名1=? and カラム名2=?", 値1, 値2)
- または(or)の指定
(条件).or(モデル名.where(条件))
- または(or)の指定
("カラム名1=? or カラム名2=?", 値1, 値2)
- 否定
where.not(条件)
- nil以外
where.not(カラム名: nil)
- パターンを指定
("カラム名 like?", "パターン")
- 否定パターンを指定
("カラム名 not like?", "パターン")
whereメソッドの詳細については以下ページをご参考ください。
【Rails】whereメソッドの使い方まとめ。モデルを使って取得するテーブルの検索条件の絞り込みをする方法
実例
#idカラムの値が8, 9, 14のデータを検索
irb(main):046:0> Test.where(id:[8,9,14])
Test Load (0.6ms) SELECT "tests".* FROM "tests" WHERE "tests"."id" IN ($1, $2, $3) /* loading for inspect */ LIMIT $4 [["id", 8], ["id", 9], ["id", 14], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Test id: 8, name: "ユーザー1", created_at: "2021-07-20 00:30:22.334075000 +0000", updated_at: "2021-07-20 00:30:22.334075000 +0000">, #<Test id: 9, name: "ユーザー2", created_at: "2021-07-20 00:30:31.939292000 +0000", updated_at: "2021-07-20 00:30:31.939292000 +0000">, #<Test id: 14, name: "ユーザー7", created_at: "2021-07-20 02:14:32.483212000 +0000", updated_at: "2021-07-20 02:14:32.483212000 +0000">]>
#カラムageが16以下を検索
irb(main):068:0> Student.where("age <= ? ", 16)
Student Load (0.9ms) SELECT "students".* FROM "students" WHERE (age <= 16 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 3, name: "ケン", age: 16, gender: "男性", created_at: "2021-07-20 01:19:01.170922000 +0000", updated_at: "2021-07-20 01:19:01.170922000 +0000">]>
#whereをつなげて絞り込み(AND)
irb(main):080:0> Student.where(gender: "女性").where("age > ?", 16)
Student Load (0.8ms) SELECT "students".* FROM "students" WHERE "students"."gender" = $1 AND (age > 16) /* loading for inspect */ LIMIT $2 [["gender", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
#25歳以上か女性を検索
irb(main):095:0> Student.where("age >= ? or gender = ? ", 25, 1)
Student Load (0.9ms) SELECT "students".* FROM "students" WHERE (age >= 25 or gender = 1 ) /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
#nameカラムに「ー」を含む
irb(main):104:0> Student.where("name like?", "%ー%")
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE (name like'%ー%') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 1, name: "イチロー", age: 24, gender: "男性", created_at: "2021-07-20 01:18:23.633399000 +0000", updated_at: "2021-07-20 01:18:23.633399000 +0000">, #<Student id: 2, name: "ジェシー", age: 17, gender: "女性", created_at: "2021-07-20 01:18:44.653383000 +0000", updated_at: "2021-07-20 01:18:44.653383000 +0000">, #<Student id: 4, name: "ジェニファー", age: 25, gender: "女性", created_at: "2021-07-20 01:19:15.589040000 +0000", updated_at: "2021-07-20 01:19:15.589040000 +0000">]>
#nameカラムに「ー」を含まない
irb(main):111:0> Student.where("name not like?", "%ー%")
Student Load (0.7ms) SELECT "students".* FROM "students" WHERE (name not like'%ー%') /* loading for inspect */ LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Student id: 3, name: "ケン", age: 16, gender: "男性", created_at: "2021-07-20 01:19:01.170922000 +0000", updated_at: "2021-07-20 01:19:01.170922000 +0000">, #<Student id: 5, name: "Danny", age: 32, gender: "男性", created_at: "2021-07-20 01:19:43.297445000 +0000", updated_at: "2021-07-20 01:19:43.297445000 +0000">]>
テーブルに行を追加する(データの追加)
テーブルにデータ(行)を追加したい場合は次のコードを実行します。
モデル名.create(カラム名: 値)
データが複数ある場合は、カンマ「,」で区切ってカラム名: 値
をつなげていきます。
モデル名.create(カラム名1: 値1, カラム名2: 値2, カラム名3: 値3, ,,,)
文字列はクオテーションで囲みます。(ダブルクオテーションでも大丈夫です)
実例
#テーブル一覧を表示
irb(main):001:0> ActiveRecord::Base.connection.tables
=> ["schema_migrations", "ar_internal_metadata", "clients", "active_admin_comments", "tests"]
#clientsテーブルのカラム一覧を表示
irb(main):003:0> Client.column_names
=> ["id", "pj_name", "client_name", "status", "order_date", "price", "memo", "created_at", "updated_at"]
irb(main):024:0> Client.create(pj_name:"最初のPJ", client_name:"クライアント1", status:0, order_date:"2021-1-1", price:4000)
TRANSACTION (0.5ms) BEGIN
Client Create (4.1ms) INSERT INTO "clients" ("pj_name", "client_name", "order_date", "price", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["pj_name", "最初のPJ"], ["client_name", "クライアント1"], ["order_date", "2021-01-01"], ["price", 4000], ["created_at", "2021-07-20 00:24:45.008452"], ["updated_at", "2021-07-20 00:24:45.008452"]]
TRANSACTION (1.2ms) COMMIT
=> #<Client id: 8, pj_name: "最初のPJ", client_name: "クライアント1", status: "契約前", order_date: "2021-01-01", price: 4000, memo: nil, created_at: "2021-07-20 00:24:45.008452000 +0000", updated_at: "2021-07-20 00:24:45.008452000 +0000">
一部のデータを変更する
一部のデータを変更するメソッドはバリデーションを実行するかや結果の返し方の違いで、いくつかのメソッドが用意されています。
メソッド | バリデーション | 補足 |
---|---|---|
update | 〇 | バリデーションの実行結果をtrueかfalseで返す |
update! | 〇 | バリデーションでエラーが発生した場合、例外を返す |
update_attributes | 〇 | updateのエイリアス |
update_all | × | 非推奨。すべての値を変更 |
update_attribute | × | 非推奨 |
既に存在するデータを変更したい場合はupdate
を使います。
モデル名.メソッド で絞り込んだ条件に対してupdateを実行します。(モデル名やモデル名.allを指定した場合は、すべての行のデータが同じ値になります)
対象.update(カラム名1:値1, カラム名2:値2, カラム名3:値3,,,)
実例
#updateの例
irb(main):088:0> Client.find(45).update(age:43)
Client Load (0.7ms) SELECT "clients".* FROM "clients" WHERE "clients"."id" = $1 LIMIT $2 [["id", 45], ["LIMIT", 1]]
=> true
#update_allの例
irb(main):091:0> Client.update_all(age:32)
Client Update All (8.4ms) UPDATE "clients" SET "age" = $1 [["age", "32"]]
=> 5
データを削除する方法
不要な行を削除する方法は2つあります。
- destroy: 渡された1つのデータのみを削除(1つ以上の場合はエラーになる)
- destroy_all : 渡されたデータをすべて削除
destroy: 渡された1つのデータのみを削除
destroyメソッドを使うと、渡された1つのデータのみを削除することができます。
対象はオブジェクトのみです。ActiveRecord::Relationに対しては使えません(エラーになります)。
モデル名.対象を抽出するメソッド.destroy
実例
#3番目の行を削除
irb(main):032:0> Client.third.destroy
Client Load (0.6ms) SELECT "clients".* FROM "clients" ORDER BY "clients"."id" ASC LIMIT $1 OFFSET $2 [["LIMIT", 1], ["OFFSET", 2]]
TRANSACTION (0.5ms) BEGIN
Client Destroy (0.5ms) DELETE FROM "clients" WHERE "clients"."id" = $1 [["id", 41]]
TRANSACTION (4.9ms) COMMIT
=> #<Client id: 41, created_at: "2021-07-24 04:21:15.273201000 +0000", updated_at: "2021-07-24 04:21:15.273201000 +0000", name: "test3", age: "22">
#age=20となる行を削除
▼エラー例
#複数の行を削除しようとするとエラーになる
irb(main):016:0> Client.all.destroy
Traceback (most recent call last):
ArgumentError (wrong number of arguments (given 0, expected 1))
#whereメソッドの結果に使うとエラーになる
irb(main):050:0> Client.where(name:"test").destroy
Traceback (most recent call last):
1: from (irb):50
ArgumentError (wrong number of arguments (given 0, expected 1))
destroy_all : 渡されたデータをすべて削除
destroy_allメソッドを使うと、渡されたすべてのデータを削除することができます。
対象はActiveRecord::Relationのみです。オブジェクトに対しては使えません(エラーになります)。
モデル名.対象を抽出するメソッド.destroy_all
実例
#すべてのデータを削除
irb(main):069:0> Client.all.destroy_all
Client Load (0.9ms) SELECT "clients".* FROM "clients"
TRANSACTION (0.3ms) BEGIN
Client Destroy (0.7ms) DELETE FROM "clients" WHERE "clients"."id" = $1 [["id", 42]]
TRANSACTION (6.0ms) COMMIT
TRANSACTION (0.4ms) BEGIN
Client Destroy (0.6ms) DELETE FROM "clients" WHERE "clients"."id" = $1 [["id", 44]]
TRANSACTION (4.2ms) COMMIT
=> [#<Client id: 42, created_at: "2021-07-24 04:21:19.354113000 +0000", updated_at: "2021-07-24 04:21:19.354113000 +0000", name: "test4", age: "24">, #<Client id: 44, created_at: "2021-07-24 06:02:42.058460000 +0000", updated_at: "2021-07-24 06:02:42.058460000 +0000", name: "test", age: "30">]
#whereメソッドの結果を削除
irb(main):068:0> Client.where("name = ?", "test5").destroy_all
Client Load (0.9ms) SELECT "clients".* FROM "clients" WHERE (name = 'test5')
TRANSACTION (0.4ms) BEGIN
Client Destroy (0.6ms) DELETE FROM "clients" WHERE "clients"."id" = $1 [["id", 43]]
TRANSACTION (6.4ms) COMMIT
=> [#<Client id: 43, created_at: "2021-07-24 04:21:24.925456000 +0000", updated_at: "2021-07-24 04:21:24.925456000 +0000", name: "test5", age: "26">]
▼エラー例
#オブジェクトに対しては使えない(find_byメソッド)
irb(main):065:0> Client.find_by(name:"test").destroy_all
Client Load (0.7ms) SELECT "clients".* FROM "clients" WHERE "clients"."name" = $1 LIMIT $2 [["name", "test"], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):65
NoMethodError (undefined method `destroy_all' for #<Client:0x000056459ba04c18>)
Did you mean? destroy
#オブジェクトに対しては使えない(findメソッド)
irb(main):066:0> Client.find(42).destroy_all
Client Load (0.5ms) SELECT "clients".* FROM "clients" WHERE "clients"."id" = $1 LIMIT $2 [["id", 42], ["LIMIT", 1]]
Traceback (most recent call last):
2: from (irb):65
1: from (irb):66:in `rescue in irb_binding'
NoMethodError (undefined method `destroy_all' for #<Client:0x000056459b1e49e8>)
Did you mean? destroy
テーブルのカラムを追加・名前変更・削除・データ型を変更する方法
モデル経由でテーブルを操作できるのは、テーブルや中身の表示、一部のデータの追加・変更・削除です。
テーブルのカラムの追加・名前変更・削除・データ型を変更といったスキーマ自体の変更はマイグレーションファイル経由で行う必要があります。
テーブルのカラムを追加・名前変更・削除・データ型を変更する方法については下記をご参考ください。
【Rails】テーブルやカラムの追加・名前の変更・削除・データ型を変更する方法を実例で解説
テーブルを削除する方法
テーブルの削除も、テーブルのカラム自体の変更と同じく、スキーマの変更となるのでモデル経由で行うことはできません。マイグレーションファイル経由で行う必要があります。
テーブルの削除方法と注意点については下記をご参考ください。
【Rails】テーブル削除(drop_table)の注意点! エラー対処法:ActiveRecord::IrreversibleMigration