【Rails入門】モデル(model)の基本まとめ

Ruby on Rails(以降、Rails)は、MVC呼ばれるデザインパターンを採用したフレームワークです。

この記事では、そのMVCのうちのM、すなわちModelについて解説します。

  • そもそもMVCとは
  • そしてModelとは
  • どのようにしてModelを作成するか

といった基本的な内容から、

  • データの取り出し方
  • データの更新方法
  • データの新規作成

などの実務的な内容に関しても解説していきます。Modelの基礎を正しく理解し、スムーズに応用できるように、わかりやすく解説します!

目次

Modelとは

Modelは、MVCを構成するコンポーネントの1つです。MVCは、以下の3つのコンポーネント(構成要素)から構成されるデザインパターンです。

コンポーネント(構成要素)説明備考
Model(モデル)データベースを取り扱うデータベースへの格納方法は、Modelに隠ぺいする
View(ビュー)画面表示を取り扱う表示方法は、Viewに隠ぺいする
Controller(コントローラー)(ユーザーの入力を受けて)ModelとViewにアレコレ指示するデータベースへの格納方法や表示方法は知らない

Railsでは、Rails標準のライブラリであるActive Recordを利用して、Modelを実装します。つまり、Railsでは、Active Recordを使って、データベース(テーブル)にデータを格納したり更新したり、データベース(テーブル)に格納したデータを検索したりできるということです。

Modelの使い方を理解するために環境を準備しよう!

まずはRailsの開発環境を構築しよう

Modelの使い方を理解するために、Railsの開発環境を構築しておきましょう。Railsの開発環境の構築方法は、以下の記事で解説していますので、ぜひご覧ください。

この記事では、app/samurai/model-demoディレクトリを作成して開発環境を構築した場合を例に、説明を続けます。

hirb gemとhirb-unicode gemをインストールしよう

この後、Modelの使い方を確認する際、Railsコンソールを使用します。Railsコンソールで出力したデータの見栄えを良くにするために、以下の2つのgemをインストールしましょう。

gem説明
hirb gem出力結果を表形式で出力する
hirb-unicode gemマルチバイト文字の表示を補正する

(1)Gemfileの最終行に以下の内容を追記します。

gem 'hirb'
gem 'hirb-unicode'

(2)以下のコマンドを入力します。

bundle install

これで、hirb gemとhirb-unicode gemがインストールされました。

Modelを作成する

RailsでModelを作成するには、以下の2つを作成します。

  • モデルクラス(Active Record)
  • データベース(テーブル)

この記事では、movieというModelと、directorというModelを作成します。movie Modelでは、以下の情報を格納します。

カラムデータ型説明
titlestring映画のタイトル
publisheddate映画が公開された日付
salesbigint映画の興行収入(単位:ドル)
rankinteger興行収入順位
directorreferences
(belongs_toでも同じ)
監督(外部キー)

director Modelでは、以下の情報を格納します。

カラムデータ型説明
namestring監督名

Railsで上記のようなModelを簡単に作成するために、bin/rails generate modelコマンドと、bin/rails db:migrateコマンドが用意されています。まずは、bin/rails generate modelコマンドの基本構文を説明します。

rails generate model name field:type [...]

パラメータ説明
name生成するモデルクラスの名前
fieldフィールド名
typeデータの型など
代表的な型は以下のとおりです。
・string
・date
・integer
・bigint
・references / belongs_to

基本構文をチラ見したら、bin/rails generate modelコマンドの使用例を見てみましょう。

(1)「端末」を起動して、以下のコマンドを1行ずつ順番に入力します。

cd app/samurai/model-demo/
bin/rails generate model movie title:string published:date sales:bigint rank:integer director:references

実行結果:

Running via Spring preloader in process 3683
      invoke  active_record
      create    db/migrate/20180705061405_create_movies.rb
      create    app/models/movie.rb
      invoke    test_unit
      create      test/models/movie_test.rb
      create      test/fixtures/movies.yml

実行結果にあるとおり、4つのファイルが作成されていますが、この記事では、以下の3つのファイルに注目します。

ファイル名ファイルの種類説明
db/migrate/
20180705061405_create_movies.rb
マイグレーションファイルデータベース(テーブル)の作成方法を記述したファイルです。
ファイル名の先頭の数字部分は、コマンドを実行した日時により異なります。
app/models/movie.rbモデルクラスファイルモデルクラスを定義するファイルです。
test/fixtures/movies.ymlフィクスチャファイルデータベース(テーブル)に格納するデータを記述するファイルです。

各ファイルの内容は以下のとおりです。マイグレーションファイル(db/migrate/20180705061405_create_movies.rb)

class CreateMovies < ActiveRecord::Migration[5.1]
  def change
    create_table :movies do |t|
      t.string :title
      t.date :published
      t.bigint :sales
      t.integer :rank
      t.references :director, foreign_key: true

      t.timestamps
    end
  end
end

モデルクラス(app/models/movie.rb)

class Movie < ApplicationRecord
  belongs_to :director
end

フィクスチャファイル(test/fixtures/movies.yml)

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  title: MyString
  published: 2018-07-05
  sales:
  rank: 1
  director: one

two:
  title: MyString
  published: 2018-07-05
  sales:
  rank: 1
  director: two

続けて、director(外部キー)が参照するテーブルも作成しましょう。

(2)以下のコマンドを入力します。

bin/rails generate model director name:string

実行結果:

Running via Spring preloader in process 3775
      invoke  active_record
      create    db/migrate/20180705061458_create_directors.rb
      create    app/models/director.rb
      invoke    test_unit
      create      test/models/director_test.rb
      create      test/fixtures/directors.yml

movieと同様、4つのファイルが作成されます。

次に、作成されたマイグレーションファイルを元に、データベース(movieテーブル、directorテーブル)を作成します。

(2)以下のコマンドを入力します。

bin/rails db:migrate

実行結果:

== 20180705061405 CreateMovies: migrating =====================================
-- create_table(:movies)
   -> 0.0118s
== 20180705061405 CreateMovies: migrated (0.0120s) ============================

== 20180705061458 CreateDirectors: migrating ==================================
-- create_table(:directors)
   -> 0.0017s
== 20180705061458 CreateDirectors: migrated (0.0018s) =========================

以上で、モデルクラス(Active Record)とデータベース(movieテーブル、directorテーブル)が作成され、すなわちmovie Modelとdirector Modelが作成されたことになります。

マイグレーションファイルを作成しない方法について(–skip-migration)

ちなみに、マイグレーションファイルを作成せずに、それ以外のファイルだけを作成する場合は、以下のように–skip-migrationを指定します。

bin/rails generate model movie title:string published:date sales:bigint rank:integer director:references --skip-migration

実行結果:

Running via Spring preloader in process 4100
      invoke  active_record
      create    app/models/movie.rb
      invoke    test_unit
      create      test/models/movie_test.rb
      create      test/fixtures/movies.yml

上で紹介した実行結果と比べると、マイグレーションファイルが作成されていないことがわかりますね。

Modelに関する命名規約について

Modelに関連する命名規約をまとめました。頭文字の大文字/小文字と、単数形/複数形の違いを覚えておくとスムーズに作業を進められるでしょう。

ファイルの種類命名規約
モデルクラスファイル名頭文字が小文字、単数形movie.rb
モデルクラス名頭文字が大文字、単数形Movie
テーブル名頭文字が小文字、複数形movies

モデルクラスをカスタマイズする

bin/rails generate modelコマンドで作成されるモデルクラスは、極めて基本的な内容しか書かれていません。

モデルクラスには、データベースを取り扱う(データベースへの格納方法は、Modelに隠ぺいする)という役割がありますので、その役割から逸脱しないようにプログラムを書いていきましょう。ここでは、モデルクラスでよく使う機能を紹介します。

検証機能を付与する(validates)

モデルクラスをカスタマイズして、検証機能を付与できます。検証機能について詳しくは、以下の記事を参考にしてください。

クエリを定義する(scope)

モデルクラスでよく使うクエリを、モデルクラスファイルで定義できます。クエリを定義する方法について詳しくは、以下の記事を参考にしてください。

Modelを削除する

もし間違ったModelを作成してしまった場合は、モデルクラスとテーブルを削除します。

モデルクラスを削除する

モデルクラスを削除するには、以下のコマンドを入力します。

bin/rails destroy model movie

実行結果:

Running via Spring preloader in process 3946
      invoke  active_record
      remove    db/migrate/20180705061405_create_movies.rb
      remove    app/models/movie.rb
      invoke    test_unit
      remove      test/models/movie_test.rb
      remove      test/fixtures/movies.yml

テーブルを削除する

モデルクラスを削除したら、テーブルを削除しましょう。

bin/rails db:drop:all

実行結果:

Dropped database 'db/development.sqlite3'
Database 'db/test.sqlite3' does not exist
Database 'db/production.sqlite3' does not exist

ここでは、すべてのテーブルを削除しましたが、必要なテーブルがある場合は、以下のコマンドを実行すると良いでしょう。

bin/rails db:migrate:reset

モデルクラスを利用する

Modelを作成できたところで、Modelを扱ってみましょう。RailsでModelを扱うには、モデルクラスを利用して、テーブルに格納したデータを検索したり更新します。

テストデータを準備する

まずは、テストデータを準備します。この記事では、テーブルへテストデータを格納するために、フィクスチャを利用します。

(1)test/fixtures/movies.ymlを以下のように編集します。

変更前:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  title: MyString
  published: 2018-07-05
  sales:
  rank: 1
  director: one

two:
  title: MyString
  published: 2018-07-05
  sales:
  rank: 1
  director: two

変更後:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  title: アバター
  published: 2009-12-18
  sales: 2787965087
  rank: 0
  director: jc

two:
  title: タイタニック
  published: 1997-12-19
  sales: 2187463944
  rank: 0
  director: jc

three:
  title: スター・ウォーズ/フォースの覚醒
  published: 2015-12-18
  sales: 2068223624
  rank: 0
  director: jja

director:に指定したjcやjjaは、次のtest/fixtures/directors.ymlで指定するラベルに対応しています。

(2)test/fixtures/directors.ymlを以下のように編集します。

変更前:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  name: MyString

two:
  name: MyString

変更後:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

jc:
  name: James Cameron

jja:
  name: J.J. Abrams

jr.ar:
  name: Joe Russo, Anthony Russo

以上がテストデータです。このテストデータをテーブルに格納しましょう。

(3)以下のコマンドを入力します。

bin/rails db:fixtures:load

テーブルに格納できたことを確認しましょう。

(4)以下のコマンドを1行ずつ順番に入力します。

bin/rails console
Hirb.enable
Movie.all

実行結果:

  Movie Load (1.4ms)  SELECT "movies".* FROM "movies"
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| id        | title                  | published  | sales      | rank | director_id | created_at            | updated_at             |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| 113629430 | スター・ウォーズ/フ... | 2015-12-18 | 2068223624 | 0    | 530990343   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
| 298486374 | タイタニック           | 1997-12-19 | 2187463944 | 0    | 175153456   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
| 980190962 | アバター               | 2009-12-18 | 2787965087 | 0    | 175153456   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
3 rows in set

(5)以下のコマンドを1行ずつ順番に入力します。

Director.all

実行結果:

  Director Load (0.2ms)  SELECT "directors".* FROM "directors"
+-----------+--------------------------+-------------------------+-------------------------+
| id        | name                     | created_at              | updated_at              |
+-----------+--------------------------+-------------------------+-------------------------+
| 175153456 | James Cameron            | 2018-07-05 06:25:10 UTC | 2018-07-05 06:25:10 UTC |
| 530990343 | J.J. Abrams              | 2018-07-05 06:25:10 UTC | 2018-07-05 06:25:10 UTC |
| 615043578 | Joe Russo, Anthony Russo | 2018-07-05 06:25:10 UTC | 2018-07-05 06:25:10 UTC |
+-----------+--------------------------+-------------------------+-------------------------+
3 rows in set

無事にテストデータが格納されていますね!

(6)以下のコマンドを入力します。

exit

検索してデータを取り出す

モデルクラスを利用して、テーブルからデータを検索して取り出す方法を紹介します。ここではRailsコンソールを使っていますが、モデルクラスファイルでも同様のコードでデータを検索して取り出せます。

(1)以下のコマンドを入力します。

bin/rails console

ここでは、whereメソッドを使って、2001-01-01以降に公開された映画のデータを取り出してみましょう。

(2)以下のコマンドを1行ずつ順番に入力します。

Hirb.enable
Movie.where('published > "2001-01-01"')

実行結果:

  Movie Load (1.6ms)  SELECT "movies".* FROM "movies" WHERE (published > "2001-01-01")
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| id        | title                  | published  | sales      | rank | director_id | created_at            | updated_at             |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| 113629430 | スター・ウォーズ/フ... | 2015-12-18 | 2068223624 | 0    | 530990343   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
| 980190962 | アバター               | 2009-12-18 | 2787965087 | 0    | 175153456   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
2 rows in set

該当するデータが2件取得できていますね。

(3)以下のコマンドを入力します。

exit

データを更新する

次に、テーブルのデータを更新してみましょう。やはりRailsコンソールを使います。

(1)以下のコマンドを入力します。

bin/rails console

ここでは、whereメソッドを使って、title(映画のタイトル)が「アバター」のデータを取り出して、rank(興行収入順位)を1にしてみましょう。

(2)以下のコマンドを1行ずつ順番に入力します。

Hirb.enable
Movie.where('title = "アバター"').update(rank: '1')

実行結果:

  Movie Load (1.8ms)  SELECT "movies".* FROM "movies" WHERE (title = "アバター")
   (0.1ms)  begin transaction
  Director Load (0.1ms)  SELECT  "directors".* FROM "directors" WHERE "directors"."id" = ? LIMIT ?  [["id", 175153456], ["LIMIT", 1]]
  SQL (0.8ms)  UPDATE "movies" SET "rank" = ?, "updated_at" = ? WHERE "movies"."id" = ?  [["rank", 1], ["updated_at", "2018-07-05 06:27:35.943715"], ["id", 980190962]]
   (16.1ms)  commit transaction
+-----------+----------+------------+------------+------+-------------+-------------------------+-------------------------+
| id        | title    | published  | sales      | rank | director_id | created_at              | updated_at              |
+-----------+----------+------------+------------+------+-------------+-------------------------+-------------------------+
| 980190962 | アバター | 2009-12-18 | 2787965087 | 1    | 175153456   | 2018-07-05 06:25:10 UTC | 2018-07-05 06:27:35 UTC |
+-----------+----------+------------+------------+------+-------------+-------------------------+-------------------------+
1 row in set

rankの値が「0」から「1」に変更されました。

(3)以下のコマンドを入力します。

exit

データを作成する

最後に、新しいデータを作成してテーブルに追加してみましょう。ここでもRailsコンソールを使います。

(1)以下のコマンドを入力します。

bin/rails console

(2)以下のコマンドを1行ずつ順番に入力します。

Hirb.enable
movie = Movie.new(title: 'アベンジャーズ/インフィニティ・ウォー', published: '2018-04-27', sales: '2036816820', rank: '5', director_id: '175153456')

実行結果:

+----+---------------------------------------+------------+------------+------+-------------+------------+------------+
| id | title                                 | published  | sales      | rank | director_id | created_at | updated_at |
+----+---------------------------------------+------------+------------+------+-------------+------------+------------+
|    | アベンジャーズ/インフィニティ・ウォー | 2018-04-27 | 2036816820 | 5    | 175153456   |            |            |
+----+---------------------------------------+------------+------------+------+-------------+------------+------------+
1 row in set

データベース(テーブル)に保存します。

(3)以下のコマンドを入力します。

movie.save

実行結果:

   (0.1ms)  begin transaction
  Director Load (0.1ms)  SELECT  "directors".* FROM "directors" WHERE "directors"."id" = ? LIMIT ?  [["id", 175153456], ["LIMIT", 1]]
  SQL (1.4ms)  INSERT INTO "movies" ("title", "published", "sales", "rank", "director_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?)  [["title", "アベンジャーズ/インフィニティ・ウォー"], ["published", "2018-04-27"], ["sales", 2036816820], ["rank", 5], ["director_id", 175153456], ["created_at", "2018-07-05 06:29:34.358453"], ["updated_at", "2018-07-05 06:29:34.358453"]]
   (6.6ms)  commit transaction
=> true

新しいデータが追加されていることを確認しましょう。

(4)以下のコマンドを入力します。

Movie.all

実行結果:

  Movie Load (0.1ms)  SELECT "movies".* FROM "movies"
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| id        | title                  | published  | sales      | rank | director_id | created_at            | updated_at             |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
| 113629430 | スター・ウォーズ/フ... | 2015-12-18 | 2068223624 | 0    | 530990343   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
| 298486374 | タイタニック           | 1997-12-19 | 2187463944 | 0    | 175153456   | 2018-07-05 06:25:1... | 2018-07-05 06:25:10... |
| 980190962 | アバター               | 2009-12-18 | 2787965087 | 1    | 175153456   | 2018-07-05 06:25:1... | 2018-07-05 06:27:35... |
| 980190963 | アベンジャーズ/イン... | 2018-04-27 | 2036816820 | 5    | 175153456   | 2018-07-05 06:29:3... | 2018-07-05 06:29:34... |
+-----------+------------------------+------------+------------+------+-------------+-----------------------+------------------------+
4 rows in set

データが4つになりましたね!

まとめ

この記事では、Modelの基礎として、Modelの役割を説明しました。また、データの検索や更新を行う方法など、実務的な内容についても説明しました。Modelには、この記事で説明した内容のほかにも様々な役割や使い方があり、学ぶ範囲がとても広いテーマです。

まずは、この記事でまとめたModelの基礎をしっかり理解したうえで、さらに難しい内容に取り組んでみてはいかがでしょうか?もし、Modelの基礎を忘れてしまったら、この記事を思い出してくださいね!

この記事を書いた人

侍エンジニア塾は「人生を変えるプログラミング学習」をコンセンプトに、過去多くのフリーランスエンジニアを輩出したプログラミングスクールです。侍テック編集部では技術系コンテンツを中心に有用な情報を発信していきます。

目次