書籍転載:Ruby on Rails 4アプリケーションプログラミング
モデルの基本
Railsプログラミングの最大の特徴は「MVC」。ControllerとViewをマスターしたら、最後にModelの基本を習得しよう。データベースをO/Rマッパーで活用する。
書籍転載について
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
前回は「2.3 ビューの基本」を説明しました。本稿はその続きです。Railsアプリケーションの作成方法については、第1回から順にお読みください。
■
2.4 モデルの基本
Controller(コントローラクラス)、View(テンプレート)を理解したところで、いよいよ残るはModel(モデル)です。モデルとは、データベースや外部サービスへのアクセスをはじめ、ビジネスロジック全般を担当するコンポーネントのことです。
Rails 3以降では、以前のバージョンに較べてModularity(モジュール性)が推し進められた結果、モデル構築にもさまざまなコンポーネントを利用できるようになりましたが、初学者の方であれば、まずはRails標準のO/RマッパーであるActive Recordを利用すると良いでしょう。Active RecordはRailsの初期バージョンからRailsの標準的なモデルコンポーネントとして提供されているライブラリで、リレーショナルデータベースのデータをオブジェクト経由で操作するための手段を提供します。
2.4.1 O/Rマッパーとは?
O/R(Object/Relational)マッパーとは、リレーショナルデータベースとオブジェクト指向言語との橋渡しを受け持つライブラリのことです。
そもそもアプリケーション側で使用するオブジェクトモデルと、データベースが利用するリレーショナルモデルとは決定的に構造が異なります。このため、従来はデータベースから取得した表形式の結果を手動でオブジェクトのプロパティに割り当てたり、逆に、データベースに登録すべき値をオブジェクトからひとつひとつ取り出したり、といった手順が必要であったわけです。このような作業は単純ですが、実にアプリケーションコードの半分以上を占めていたとも言われ、開発生産性を低下させる一因にもなっていました。このようなアプリケーションとデータベースとの構造的なギャップのことをインピーダンスミスマッチと言います(図2-12)。
O/Rマッパーとは、このようなミスマッチを解消するためのツールです(図2-13)。Active Recordでは、データベースのテーブルひとつをひとつのモデルクラスとして表現します。モデルクラスのインスタンスは、レコード1件に対応するオブジェクトとなり、オブジェクトのプロパティはそのままテーブルのフィールドに対応します。
たとえば、booksというテーブルがあったとすれば、対応するモデルはBookクラスであり、Bookクラスはbooksテーブル配下のフィールドと同名の(たとえば)isbn、title、publishのようなプロパティを持つことになるでしょう(図2-14)。
O/RマッパーであるActive Recordを利用することで、Railsではリレーショナルデータベース(表形式のデータ)をあたかもオブジェクトであるかのように操作できるようになるのです。
また、O/Rマッパーを利用することで、基本的にはSQL命令を記述する必要はなくなります。SQLには往々にしてデータベース製品固有の方言が存在しますが、O/Rマッパーはそれらを内部的に吸収してくれますので、接続先のデータベースを変更した場合にもアプリケーションへの影響は最小限に抑えられます*24。
- *24 もっとも、だからといってSQLの知識が不要というわけではありません。Active Recordの構文はSQLのそれに準じていますので、SQLを理解していることは、そのままActive Recordの理解にもつながるでしょう。
2.4.2 データベース接続の設定
Active Record経由でデータベースに接続するには、まずconfig/database.ymlに対して接続設定を定義する必要があります。以下は、アプリケーション作成時にデフォルトで用意されているdatabase.ymlです*25(リスト2- 6)。
開発
環境の
設定
テスト
環境の
設定
本番
環境の
設定
|
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
test:
adapter: sqlite3
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
pool: 5
timeout: 5000
|
- *25 実際にはコメントが含まれていますが、紙面上は見やすさのために割愛しています。
以下に、database.ymlを編集する上で知っておきたいポイントをまとめます。
1 database.ymlはYAML形式で記述する
YAML(ヤムル)は構造化データの記述に適したファイル形式です。「YAML Ain't Markup Language(YAMLはマークアップ言語ではありません)」というその名の通り、HTMLやXMLのようなマークアップ言語ではなく、構造をインデントや記号で表現します。
複雑な構造を表現するには不向きですが*26、読みやすさやシンプルさの点でXMLよりも優れており、近年ではさまざまなフレームワークで採用される機会も増えています。
- *26 もっとも、一般的には、XMLでなければ表現できないような構造を、設定ファイルで記述することはまずないでしょう。
YAMLでは、「パラメータ名: 値」の形式でパラメータを表すのが基本です。また、階層はインデントで表現します。たとえば、
production:
adapter: sqlite3
|
は、productionパラメータのサブパラメータadapter(値はsqlite3)を表します。ただし、インデントではタブ文字を利用できない点に注意してください。インデントは必ず空白(一般的には半角スペース2つ)で表現します。
2 Railsは目的に応じて環境を使い分ける
Railsでは、development(開発)、test(テスト)、production(本番)環境が用意されており、目的に応じて使い分けるのが基本です。database.ymlでもそれぞれの環境単位に設定が分けられており、別々のデータベースを用意するようになっています。これによって、たとえば開発環境で行った操作が不用意に本番環境に影響を及ぼすような事故を防げるわけです。
それぞれの環境に対して設定できるパラメータの内容は、表2-4の通りです(データベースによって指定できるパラメータは異なる可能性もあります)。デフォルトではdevelopment環境が使われますので、変更が必要である場合はまずdevelopmentパラメータの配下を編集してください*27。最低限の設定は既になされていますので、まずはそのままでも問題ないはずです。
- *27 実行環境を変更する方法については、2.4.8項も参照してください。
パラメータ名 | 概要 |
---|---|
adapter | 接続するデータベースの種類(sqlite3、mysql2、postgresqlなど) |
database | データベース名(SQLiteではデータベースファイルのパス) |
host | ホスト名/IPアドレス |
port | ポート番号 |
pool | 確保する接続プール*28 |
timeout | 接続のタイムアウト時間(ミリ秒) |
encoding | 使用する文字コード |
username | ユーザ名 |
password | パスワード |
socket | ソケット(/tmp/mysql.sockなど) |
- *28 データベースへの接続をあらかじめ準備(プール)しておき、利用後は(切断するのではなく)プールに戻して再利用するしくみを言います。これによって、接続のオーバヘッドを軽減できるというメリットがあります。
データベース名はデフォルトで「環境名.sqlite3」となっていますが、必要に応じて変更しても構いません*29。
- *29 特に環境名(development、productionなど)はタイプするのも面倒なので、「dev」「pro」などのように省略しても良いでしょう。
2.4.3 モデルクラスの作成
続いて、データベースのテーブルにアクセスするためのモデルクラスを作成します。これには、コントローラクラスの作成でも利用したrails generateコマンドを使用します。
【構文】rails generateコマンド(モデルの作成)
rails generate model name field :type […] [options]
|
name: モデル名
field: フィールド名
type: データ型
options: 動作オプション(表2-5を参照)
オプション | 概要 | デフォルト |
---|---|---|
--indexes | 外部キー列にインデックスを付与するか | true |
-o、--orm=NAME | 使用するO/Rマッパー | active_record |
--migration | マイグレーションファイルを生成するか | true |
--timestamps | タイムスタンプ(created_at、updated_at)列を生成するか | true |
-t、--test-framework=NAME | 使用するテストフレームワーク | test_unit |
--fixture | フィクスチャを生成するか | true |
- *30 この他にも、表2-2の基本オプションは共通で利用できます。
ここでは、書籍情報を表2-6のようなbooksテーブルで管理するものとし、これに対応するBookクラスを作成してみましょう。
列名 | データ型*31 | 概要 |
---|---|---|
isbn | string | ISBNコード |
title | string | 書名 |
price | integer | 価格 |
publish | string | 出版社 |
published | date | 刊行日 |
cd | boolean | CD 添付? |
- *31 利用できるデータ型については、5.8.2項(※転載対象外です)を参照してください。
以下は、rails generateコマンドとその実行結果です。列の定義が含まれているので、これまでよりも長いコマンドになっていますが、基本は繰り返しの記述ですので、間違えないようにタイプしてください*32。
> cd c:\data\railbook
> rails generate model book isbn:string title:string price:integer publish:string published:date cd:boolean
invoke active_record
create db/migrate/20130901051910_create_books.rb
create app/models/book.rb
invoke test_unit
create test/models/book_test.rb
create test/fixtures/books.yml
|
- *32 コマンドは、配布サンプル(1.2.4項)配下のcommand.txtにも掲載しています。いちいちタイプするのが面倒という方は、こちらをコピーして利用しても構いません。
この結果、アプリケーションルートの配下には、図2-15のようなファイルが生成されます。
/railbook | …… アプリケーションルート |
---|---|
├─ /app | |
│ └─ /models | |
│ └─ book.rb | …… モデルクラス(booksテーブルを操作するためのモデル本体) |
├─ /db | |
│ └─ /migrate | |
│ └─ 20130913051910_create_books.rb*33 | …… マイグレーションファイル |
└─ /test | |
├─ /fixtures | |
│ └─ books.yml | …… テストデータを投入するためのフィクスチャファイル |
└─ /models | |
└─ book_test.rb | …… モデルクラスをテストするためのスクリプト |
- *33 ファイル名先頭の「20130913051910」の部分は、作成した日時によって変動します。
さまざまなファイルが自動生成されますが、詳しくは徐々に見ていくとして、ここではとりあえず生成されたファイル(クラス)の命名ルールを確認しておきましょう*34(表2-7)。
種類 | 概要 | 名前(例) |
---|---|---|
モデルクラス | 先頭は大文字で単数形 | Book |
モデルクラス(ファイル名) | 先頭は小文字で単数形 | book.rb |
テーブル | 先頭は小文字で複数形 | books |
テストスクリプト | xxxxx _test.rb(先頭は小文字で単数形) | book_test.tb |
- *34 1.1.3項でも触れたように、Railsの基本は「設定よりも規約」です。Railsを正しく理解する第一歩は、命名の規約を理解することです。
先ほども述べたように、モデルクラス(正確には、そのインスタンス)はそれぞれテーブルの各行を表すので単数形に、テーブルはモデルの集合体という意味で複数形になるわけです。
2.4.4 マイグレーションファイルによるテーブルの作成
rails generateコマンドを実行しただけでは、まだ肝心のデータベース(テーブル)が作成できていません。ここでいよいよデータベースの作成に取り掛かりましょう。
Railsではテーブルの作成や修正にマイグレーションという機能を利用します。マイグレーションとは、一言で言うならば、テーブルレイアウトを作成/変更するためのしくみです。マイグレーションを利用することで、テーブル保守の作業を半自動化できるのみならず、途中でレイアウト変更が生じた場合にも簡単に反映できます。
マイグレーションを実行するためのマイグレーションファイル*35は、前項でrailsgenerateコマンドを実行した時に、既に「20130913051910_create_books.rb」のような名前で自動生成されているはずです。中身も確認しておきましょう(リスト2-7)。
- *35 マイグレーションスクリプトとも言います。
class CreateBooks < ActiveRecord::Migration
def change
create_table :books do |t|
t.string :isbn
t.string :title
t.integer :price
t.string :publish
t.date :published
t.boolean :cd
t.timestamps
end
end
end
|
changeメソッドの中で呼び出しているcreate_tableメソッドに注目してみましょう。これがbooksテーブルを新規に作成するためのコードです。
詳しい構文については第5章(※転載対象外)に譲りますが、booksという名前のテーブルに対して、isbn、title、price、publish、published、cdといったフィールドを定義していることは直感的に見て取れるでしょう。こうした列定義が、先ほどのrails generateコマンドに渡した情報によって自動生成されているわけです。
まずは最低限のテーブルレイアウトを定義するだけであれば、リスト2-7はそのまま実行できます。マイグレーションファイルを実行するのはrake db:migrateコマンドの役割です。コマンドプロンプトから以下のように実行してください*36。
> cd c:\data\railbook
> rake db:migrate
== CreateBooks: migrating ====================================================
-- create_table(:books)
-> 0.0090s
== CreateBooks: migrated (0.0100s) ===========================================
|
- *36 これまで利用してきたrailsコマンドではありません。間違えないように注意してください。
コマンドを実行するにあたって、パラメータの指定などは必要ありません。データベースへの接続設定(database.yml)や実行すべきマイグレーションファイルなどは、Railsが自動的に判定してくれるためです*37。
- *37 実行済みのマイグレーションファイルもRailsが記憶していますので、繰り返しrake db:migrateコマンドを実行しても、同じマイグレーションファイルが実行されることはありません。
上のような結果が得られれば、booksテーブルは正しく作成できています。
2.4.5 フィクスチャによるテストデータの準備
もっとも、テーブルを作成しただけではデータの取得などを確認するのに不都合ですので、テストデータも準備しておきましょう。
Railsではテストデータをデータベースに流し込むためのしくみとして、フィクスチャという機能を提供しています。フィクスチャとはYAML形式のデータをデータベースに流し込むためのしくみと理解しておけば良いでしょう。
詳しくは5.8.8項(※転載対象外です)で触れますので、ここでは、配布サンプル(1.2.4項)の/railbook/test/fixturesフォルダ配下からbooks.ymlを、自分のアプリケーション配下の同じフォルダにコピーしてください。あとは、以下のようにrakeコマンドを実行するだけです。
> cd c:\data\railbook
> rake db:fixtures:load FIXTURES=books
|
これで、あらかじめ用意された10件ほどのデータがbooksテーブルに展開されました。
2.4.6 補足:データベースクライアントの起動
rails dbconsoleコマンド*38を利用することで、config/database.ymlで定義した接続情報に従って、データベースクライアント*39を起動できます。マイグレーションやフィクスチャを実行した後、データベースの内容を確認する際など、ちょっとした作業に便利ですので、覚えておくと良いでしょう。
- *38 ショートカットとして、rails dbコマンドを利用しても構いません。
- *39 SQLite 3であればSQLiteクライアントです。rails dbconsoleコマンドは、その他にもMySQLやPostgreSQLに対応しています。
以下では、SQLiteクライアントを起動し、データベース配下のテーブルの一覧と、booksテーブルの構造、データの内容を確認しています。
> rails dbconsole
SQLite version 3.8.2 2013-12-06 14:53:30
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables …… テーブルの一覧を表示*40
books schema_migrations
sqlite> .schema books …… 1 booksテーブルの構造を確認
CREATE TABLE "books" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "isbn" varchar(255), "title" varchar(255), "price" integer, "publish" varchar(255), "published" date, "cd" boolean, "created_at" datetime, "updated_at" datetime);
sqlite> SELECT * FROM books; …… 2 booksテーブルの内容を確認
1|978-4-7741-5878-5|AndroidエンジニアのためのモダンJava|3360|技術評論社|2013-08-20|f|2013-10-05 06:14:30|2013-10-05 06:14:30
2|978-4-7741-5611-8|JavaScriptライブラリ実践活用|2709|技術評論社|2013-03-19|f|2013-10-05 06:14:30|2013-10-05 06:14:30
3|978-4-7741-4980-6|Ruby on Rails 4ポケットリファレンス|2919|技術評論社|2012-01-26|t|2013-10-05 06:14:30|2013-10-05 06:14:30
…後略…
sqlite> .quit …… SQLiteクライアントを終了
|
- *40 schema_migrationsテーブルは、実行済みのマイグレーションを管理するためにRailsが自動で用意したテーブルです。詳しくは5.8節(※転載対象外です)で解説します。
SQLiteクライアントでテーブルの構造(スキーマ)を確認するには.schemaメタコマンドを利用します(1)。出力されたCREATE TABLE命令をよく見てみると、自分では明示的に定義しなかったid、created_at、updated_atフィールドが含まれていることが見て取れると思います。これらもすべてRailsが予約しているフィールドで、それぞれ表2-8の役割を持ちます。
フィールド名 | 概要 |
---|---|
id | 主キー(自動連番) |
created_at | レコードの新規作成日時(Active Recordが自動セット) |
updated_at | レコードの更新日時(Active Recordが自動セット) |
よって、自分でフィールドを定義する際には、これらの名前は使用しないようにしてください。また、rails generate命令でモデルを定義する際に、主キーを意識しなくて良かったのもこのためです。
2では、SELECT命令でbooksテーブルの内容を一覧表示しています。ただし、Windows環境では文字コードの関係でマルチバイト文字(日本語)が文字化けしてしまう点に注意してください*41。
- *41 これを解消するには、コマンドプロンプトのプロパティウィンドウから[フォント]タブを選択し、[フォント]として「MSゴシック」を選択します。その上で、コマンドラインから「> chcp65001」というコマンドを実行してください。
2.4.7 データ取得の基本
Active Recordを利用する準備ができたところで、動作確認も兼ねて、ごく簡単なサンプルを作成してみましょう。ここで作成するのはbooksテーブルからすべてのデータを取得し、一覧表として整形するサンプルです(図2-16)。
では、具体的な手順を見ていきましょう。
1 listアクションを追加する
2.2.1項で作成済みのhelloコントローラに対して、リスト2-8の要領でlistアクションを追加します。
class HelloController < ApplicationController
…中略…
def list
@books = Book.all
end
end
|
booksテーブルからすべてのレコードを無条件に取得するには、2.4.3項で作成したBookオブジェクト(モデルクラス)のallメソッドを呼び出します。allメソッドはいわゆる「SELECT * FROM books」のようなSQL命令を発行するメソッドで、結果をBookオブジェクトの配列として返します。
オブジェクトのビューへの引き渡しは、2.3.1項でも述べたようにインスタンス変数を経由して行うのでした。
【NOTE】モデルクラスの中身
自動生成されたBookクラス(book.rb)をテキストエディタなどで開いてみると、実は中身はほとんど空であることがわかります(リスト2-9)。
class Book < ActiveRecord::Base
end
|
しかし、基底クラスであるActiveRecord::Baseクラスがデータベースアクセスのための基本機能を提供しているため、このままでも検索や登録などの操作が可能なのです。当面は自動生成されたモデルには手を加えず、そのまま利用していくことにします。モデルに対して(たとえば)入力値検証などの独自の機能を実装する方法については、改めて第5章(※転載対象外)で解説します。
2 テンプレートファイルを作成する
hello#listアクションに対応するテンプレートファイルlist.html.erbを作成します(リスト2-10)。テンプレートファイルは、コントローラクラスに対応するように/app/views/helloフォルダの配下に配置するのでした。
<table border="1">
<tr>
<th>ISBNコード</th><th>書名</th><th>価格</th>
<th>出版社</th><th>刊行日</th><th>CD-ROM</th>
</tr>
<% @books.each do |book| %>
<tr>
<td><%= book.isbn %></td>
<td><%= book.title %></td>
<td><%= book.price %>円</td>
<td><%= book.publish %></td>
<td><%= book.published %></td>
<td><%= book.cd %></td>
</tr>
<% end %>
</table>
|
- *42 先にも説明した通り、テンプレートファイルには、デフォルトでレイアウト(application.html.erb)が適用されます。よって、テンプレート本体にはコンテンツ本体の部分のみを記述すれば良いわけです。
オブジェクト配列の内容を順に取り出すのは、eachメソッドの役割です*43。テンプレート変数@booksにはBookオブジェクトの配列が渡されているはずなので、ここではeachメソッドで順にBookオブジェクト(ブロック変数book)を取り出し、その内容を出力しています。eachブロックの中では、book.isbnのような形式でオブジェクトの各プロパティ値(対応するフィールド値)にアクセスできます(図2-17)。
- *43 テンプレートでは標準的なRubyのスクリプトを埋め込めますので、こうした繰り返しや条件分岐などの構文を新たに覚える必要はありません。
以上の内容を理解したら、サンプルを実行してみましょう。WEBrickを起動し、ブラウザに次のアドレスを指定します。
http://localhost:3000/hello/list
本項冒頭の図2-16のように、booksテーブルの内容がグリッド表に整形されていれば、サンプルは正しく動作しています。
【NOTE】SQL命令の確認
この例ではあまり意味がないかもしれませんが、複雑な条件句を指定した場合や、意図した結果を得られない場合などは、生のSQL命令を確認することで問題を特定できることがあります。
2.4.8 補足:アプリケーションの実行環境を指定する
2.4.2項でも述べたように、Railsにはdevelopment、test、productionという環境があります。これらを変更する方法はいくつかありますが、もっとも手軽なのはWEBrickの起動時に指定するというものでしょう。
たとえば、以下のように-eオプションを指定することで、本番(production)環境でWEBrickを起動できます。
> rails server -e production
|
デフォルトは開発(development)環境となっていますので、普段はほとんど意識する必要はないと思いますが、本番環境のデータで動作を確認したいなどの用途で利用してください。
ちなみに、rails serverコマンドでは、-e以外にも以下のようなオプションを指定できます。
【構文】rails serverコマンド(HTTPサーバの起動)
rails server [name] [options]
|
name: 起動するHTTPサーバ(thinやUnicornなどWEBrick以外を起動する場合)
options: 動作オプション(表2-9を参照)
オプション | 概要 |
---|---|
-p、--port=port | 使用するポート番号(デフォルトは3000) |
-b、--binding=ip | バインドするIPアドレス(デフォルトは0.0.0.0) |
-d、--daemon | デーモンとしてサーバを起動 |
-e、--environment=name | 特定の環境(test、development、production) でサーバを起動(デフォルトは |
-P、--pid=pid | PIDファイル(デフォルトはtmp/pids/server.pid) |
-h、--help | ヘルプを表示 |
-e / --environmentオプションの他によく利用するのは、-p / --portオプションです。WEBrickのデフォルトのポート番号は3000ですが、他のアプリケーションでこのポートが使用済みであると、WEBrickの起動に失敗します。この場合には、-pオプションで以下のように指定してください。
> rails server -p 81
|
これで「http://localhost:81/ ~」のようなアドレスでアプリケーションにアクセスできるようになります。
■
次回は「2.5 Railsの設定情報」を説明します。
※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
6. 【現在、表示中】≫ モデルの基本
Railsプログラミングの最大の特徴は「MVC」。ControllerとViewをマスターしたら、最後にModelの基本を習得しよう。データベースをO/Rマッパーで活用する。
8. CoffeeScript入門(前編) ― CoffeeScriptの基本構文
Rubyプログラマーの必須知識「CoffeeScript」の基礎として、基本構文/変数とリテラル表現/演算子/制御構文を解説。書籍転載の8本目(「Part 3《応用編》 第9章 クライアントサイド開発」より)。