本当は怖くないRuby on Rails入門 Part.3

データベースの中身を表示しよう

今回はModel(モデル)を使ってDBの中身を表示してみましょう。 まずは`rails db:create`でDBを作成します。 ```bash $ rails db:create Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' ``` railsはさまざまなDBに対応していますが、デフォルトではsqlite3が使われます。sqlite3はDBはファイルとしてできます。 上記でdevelopment用のDBとtest用のDBが作成されました。

開発環境

railsでは基本的に3種類の実行モードがあります。 - production(本番) - development(開発) - test(テスト) この実行モードごとにDBが分かれています。 productionモードは速度のために無駄な処理が少なくなっています。 developmentモードはrailsサーバーの再起動無しにファイルが読み込めたりといった開発中に便利なモードです。 testモードは自動テスト実行時のモードで後々出てきます。

テーブル作成

ModelはDBのTableと一対一の関係になります。DBにFruits(フルーツ)テーブルを作って、Fruitモデルも作りましょう。 ![ss](/images/dont-be-afraid-rails/model-table.png)
Modelの名前は単数形なのにDBテーブルは複数形なんですね。
railsは名前に非常にうるさいフレームワークです。DBテーブルは1レコードにつき1fruitを表現しています。テーブルは複数レコードを持つので複数形のfruitsになります。 ![ss](/images/dont-be-afraid-rails/table.png) 単数形・複数形を変換する機能もあります。 railsの中身を手軽に試せるコマンド`rails console`で試してみましょう。 ```bash $ rails console >> "fruit".pluralize => "fruits" >> "fruits".singularize => "fruit" ``` `pluralize`は文字列を複数形にするメソッドで、`singularize`はその逆です。
細かいですね。英語の先生みたい。
`rails generate`コマンドを使ってfruitsテーブルを作成しましょう。string型のnameカラムとinteger型のpriceカラムを持ちます。 ```bash $ rails generate model fruit name:string price:integer invoke active_record create db/migrate/20180206061109_create_fruits.rb create app/models/fruit.rb invoke test_unit create test/models/fruit_test.rb create test/fixtures/fruits.yml ``` `rails generate`はさまざまなコードを生成してくれる便利なコマンドです。手で書くより早いのでどんどん活用しましょう。 - `db/migrate/20180206061109_create_fruits.rb` - `app/models/fruit.rb` が生成されたことに注目してください。(test/以下については今は気にしないでください)
ファイルが勝手にできるとびっくりしますね。

Migration(マイグレーション)

生成されたファイルを順に見ていきましょう。 `db/migrate/20180206061109_create_fruits.rb`はMigration(マイグレーション)ファイルと呼ばれるものです。 db/migrate/20180206061109_create_fruits.rb: ```ruby class CreateFruits < ActiveRecord::Migration[5.1] def change create_table :fruits do |t| t.string :name t.integer :price t.timestamps end end end ``` 普通DBテーブルを作成するにはSQLでCREATE文を使います。しかしrailsではテーブル作成や削除、カラム追加の操作はこのMigrationの仕組みを使って行います。
苦労してSQLのCREATE文を覚えたのに!CREATE文じゃ駄目なんですか?
もちろんMigrationならではの利点があります。 まずはCREATE文はPostgreSQL, MySQLなどのDBによって書き方に微妙な違いがあります。その違いをMigrationは吸収してくれます。 また、Migrationで書いておくと、一度実行したテーブル構造の変更を戻すことができます。
ええ!!そんなことできるんですか?
実際の仕組みは単純で、上記の`create_table`の場合は戻す場合にMigrationがCREATEとは逆のSQLを発行しています。 `CREATE TABLE`だったら戻す場合は`DROP TABLE`。カラム追加を戻す場合はカラム削除のSQLを発行してくれます。
へぇ〜すごい。やっぱりMigrationを使ったほうがいいですね。
もちろん中のデータが戻るわけではないので完全にDBがもとに戻るわけではないですが、これによってカラム追加や不要になったカラム削除を気軽にできます。 ファイルの中身を見ると大体わかると思いますが、`name`というstring型のカラムと`price`というinteger型のカラムを持つfruitsテーブルを作成しています。

Timestamp

`t.timestamps`というのは下記の省略記法になります。 ```ruby t.datetime :created_at t.datetime :updated_at ``` `created_at`というのはrails(Active Record)の便利機能で、モデルのデータを新規作成(DBのレコードを追加)した時、`created_at`というカラムがあったら、そこに作成した時の日付と時間を入れておいてくれます。 また、`updated_at`というカラムがあると、レコードの更新時にその時の日付と時間を入れておいてくれます。 実際にWebアプリを作った人ならわかりますが、DBテーブルには大抵こんな感じのカラムを付けることが多いので自動で付いてくれるととてもうれしいのです。
Active Record、空気の読めるヤツ・・・。

Migrationの実行と確認

それでは実際にMigrationを実行してみましょう。 ```bash $ rails db:migrate == 20180206061109 CreateFruits: migrating ===================================== -- create_table(:fruits) -> 0.0008s == 20180206061109 CreateFruits: migrated (0.0008s) ============================ ``` 下記でログを見てみると実際に発行されたSQLが確認できます。 ```bash $ cat log/development.log (省略) (0.4ms) CREATE TABLE "fruits" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "price" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) (省略) ```
あ、ほんとだ。`CREATE TABLE "fruits"`って書いてある。
sqlite3に付属のコマンドで調べてみてもしっかりテーブルが作成されています。 ```bash $ sqlite3 db/development.sqlite3 > .schema fruits CREATE TABLE "fruits" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "price" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL); ```
`id`というカラムが勝手にできてる気がするんですが?
よく気づきましたね。 Active Recordでは指定しない場合、`id`というカラムがプライマリキーとして作成されます。
プリイマリキー・・・習ったような聞いたことあるような・・・
データベースのカリキュラムで習ったはずですよ。テーブルの中でそのレコードを一意に識別するための重複する値を持たないカラムです。

データの追加

sqlite3を使ったついでにSQL文でfruitsをいくつか追加しましょう。
えー、INSERT文でしたっけ・・・書けるかなー。 ```bash $ sqlite3 db/development.sqlite3 > INSERT INTO "fruits" (name, price, created_at, updated_at) VALUES ("Apple", 100, "2018-01-01 0000", "2018-01-01 0000"); > INSERT INTO "fruits" (name, price, created_at, updated_at) VALUES ("Orange", 180, "2018-01-01 0000", "2018-01-01 0000"); > INSERT INTO "fruits" (name, price, created_at, updated_at) VALUES ("Banana", 80, "2018-01-01 0000", "2018-01-01 0000"); ``` なんとかINSERTできましたよ。 ```bash > SELECT * FROM "fruits"; id = 1 name = Apple price = 100 created_at = 2018-01-01 0000 updated_at = 2018-01-01 0000 id = 2 name = Orange price = 180 created_at = 2018-01-01 0000 updated_at = 2018-01-01 0000 id = 3 name = Banana price = 80 created_at = 2018-01-01 0000 updated_at = 2018-01-01 0000 ``` ちゃんとデータも入っています。

フルーツの一覧表示

表示するデータが整ったのでページに表示してみましょう。Part.2で出てきた図を思い出してください。 ![mvc2](/images/dont-be-afraid-rails/mvc2.png) Routesは毎回同じファイルなので省略するとして、今度は先程の`rails generate`で生成されているのでModelが出てきます。 先程の図はこうなります。 ![mvc3](/images/dont-be-afraid-rails/mvc3.png) Routes, Model, Controller, Viewを書きます。(Modelは既にあると思います) config/routes.rb: ```ruby Rails.application.routes.draw do get "home", to: "home#index" get "fruits", to: "fruits#index" end ``` app/models/fruit.rb: ```ruby class Fruit < ApplicationRecord end ``` app/controllers/fruits_controller.rb: ```ruby class FruitsController < ApplicationController def index @fruits = Fruit.all end end ``` app/views/fruits/index.html.erb: ```erb

フルーツ一覧

<% @fruits.each do |fruit| %> <% end %>
名前 値段
<%= fruit.name %> <%= fruit.price %>
``` `http://localhost:3000/fruits`にアクセスすると下記のように表示されるはずです。 ![ss](https://i.gyazo.com/4524d38426a79ed5709070b2e457e527.png)
今回はControllerのindexメソッドに何か増えてます。Viewもちょっと難しく見えます。
順を追って説明しますね。 ```ruby @fruits = Fruit.all ``` Controllerのこの部分はモデルである`Fruit`クラスを使ってDBからデータをとっています。 Modelが`rails generate`で自動生成されていることを思い出してください。中身を見てみましょう。 app/models/fruit.rb: ```ruby class Fruit < ApplicationRecord end ```
シンプル〜
Modelはテーブル名(の単数形)のクラスが`ApplicationRecord`を継承して存在しているだけでさまざまな機能が使えます。 ここで使っている`all`メソッドは単純に全部を持ってくるメソッドです。 SQLで言えば`SELECT * FROM "fruits"`をしているといえばわかりやすいでしょうか。
`all`メソッドはどこから来たんですか?`Fruit`クラスは空っぽなのに。
`Fruit`は`ApplicationRecord`を継承していますよね?`ApplicationRecord`は`app/models/application_record.rb`にあります。 そしてさらに親クラスは`ActiveRecord::Base`になっています。Active Recordのクラスが`all`や`where`などのメソッドを持っていて、それを継承してModelを作るので自分でメソッドを書かなくても最初から便利なメソッドが使えるというわけです。
機能満載のクラスを継承すると便利なんですね。
次にViewを見てみましょう。 app/views/fruits/index.html.erb: ```erb

フルーツ一覧

<% @fruits.each do |fruit| %> <% end %>
名前 値段
<%= fruit.name %> <%= fruit.price %>
```
`@fruits`にfruitsテーブルの全レコードが入っているというのはなんとなくわかりますが、Fruitクラスには1つもメソッドがなかったのに`name`や`price`メソッドが使えているのが不思議です。 これもActive Recordが持っているメソッドなんですか? でも`name`や`price`はさっき自分で決めた名前だから知らないはずですよね?
鋭いですね。それが不思議に見えるのは正しいです。Rubyの勉強をちゃんとやった証拠です。 それこそが正にrubyとActive Recordの大きなマジックです。 Active RecordがなんとDBのカラム情報を見に行って、同じ名前のメソッドを勝手に定義しているのです!
ナ、ナンダッテー!!!
カラム名と同じメソッドは全て使えると考えて大丈夫です。
rubyファイルのどこにも`name`メソッドや`price`メソッドは書いてないのに使えちゃうなんてほんと不思議ですねー。
今回はMigrationの使い方を中心にDBの情報を表示する方法を学びました。 これまでの内容を復習しておいてくださいね。特にSQLやRubyの構文が怪しい人は見直しておきましょう。
今回のPartでテーブル内容を全部出すだけだったらできるような気がしてきました! 次もがんばります!
## 前提となる技術とカリキュラム リンク先はFjord Boot Camp内のカリキュラムになっています。 - データベース - [mysqlの基本](https://bootcamp.fjord.jp/practices/21) - SQL - [sqlの基本](https://bootcamp.fjord.jp/practices/20) - ruby - [Rubyの基本](https://bootcamp.fjord.jp/practices/26)
comments powered by Disqus