7. モデル

7.1. モデルの作り方

モデルを作るには、 rails generate scaffoldrails generate model のどちらかを使う。

  • model はモデルだけを作り、 scaffold はモデルだけでなくビューやコントローラも一揃い作ってくれる。ビューやコントローラは別に作ることもできるし、作っておいて使わなくてもいいので、どちらにするかはあまり悩まくてもよい。

  • モデルの名前は単数形の名詞が推奨されている。(命名ルール)

  • モデルは関係データベースのテーブルと対応している。テーブルの構成を「カラム名:型」という形式でモデル名の後に書く。この型はRubyの型ではなくデータベースの型。よく使われる型は次の通り。

    整数

    integer

    浮動小数点数

    float

    文字列

    string

    時刻

    datetime

    真偽値

    boolean

  • どのモデルにも id というカラムが必ず存在するが、書かなくても自動的に作られる。

  • 他のモデルを参照するカラムは、 モデル名_id:integer にするのがお約束。(スキーマのルール)

ここでは、 Studentscaffold で、 Facultymodel で作ってみる。

# rails generate scaffold student name:string faculty_id:integer
# rails generate model faculty name:string

7.2. データベースのマイグレーション

rails generate scaffold または rails generate model をやった段階では、まだデータベースは出来ていない。データベースに指示を出すためのRubyファイル(マイグレーションファイル)が出来ている。それは db/migrate の下にある。

# ls db/migrate
20181029094006_create_students.rb       20181029094021_create_faculties.rb

これを実行して、データベースの中にテーブルを作ることをマイグレーションと呼ぶ。 rails db:migrate は、まだ実行されていないマイグレーションファイルを実行する(マイグレーションを実行する)。

# rails db:migrate

7.3. モデルの変更

学生のモデル Student には、氏名と学部のカラムしか無いので、学年のカラムを追加する。

7.3.1. お行儀の良いやり方

変更用のマイグレーションファイルを作る。こうすると、データベースの変更履歴がマイグレーションファイルの形で記録されるので、後で再現できる。

rails generate migration (マイグレーションを作成する) を使うと自動的にマイグレーションファイルを作ってくれる。

項目を追加する時は Add項目Toモデル という名前にし、その後に項目名と型を書く。例えば、学生のモデルに学年という項目を追加したいときは次のようにする。

# rails generate migration AddGradeToStudents grade:integer
# rails db:migrate

データベースを前の状態に戻したいときは、 rails db:rollback (ロールバック) を行う。今は CreateStudents, CreateFaculties, AddGradeToStudents の3個のマイグレーションファイルを実行した状態だが、ロールバックするごとに一段階ずつ前の状態に戻っていく。

# rails db:rollback
# rails db:rollback
# rails db:rollback

これで何もマイグレーションしていない状態になった。もう一度 rails db:migrate を実行すると、マイグレーションされていないファイルを一気に処理する。

# rails db:migrate

7.3.2. お行儀のよろしくないやり方

複数人で開発する場合や、すでに本番サーバでシステムが動いている場合は、データベースの変更を差分として管理することが必要だが、一人で新規開発しているのなら気にする必要はない。だいたい開発中の試行錯誤の履歴を残しておいても恥ずかしいだけである。

開発中は、マイグレーションファイルの中に書いてある項目名や型を直接修正し、データベースをぶっ壊してもう一度最初からマイグレーションをやり直せばよい。

例えば、 Student モデルのマイグレーションファイルは db/migrate/バージョン番号_create_studetns.rb という名前で、次のような内容になっている。

class CreateStudents < ActiveRecord::Migration[7.2]
  def change
    create_table :students do |t|
      t.string :name
      t.integer :faculty_id

      t.timestamps
    end
  end
end

学年を追加したければ、 t.string :name などと並列に t.integer :grade という行を書き込む。

データベースを一回白紙に戻してから全部作りなおすのは

# rails db:migrate:reset

とする。運用中のシステムでこれをやると、データベースに入っているデータが全部消えてしまうので注意。

7.4. モデルのクラス

それぞれのモデルごとにクラスが定義される。これは app/models の下にある。クラス定義の中身は空っぽだが、データベースにアクセスするメソッドは動的に生成されるので大丈夫。

# ls app/models
application_record.rb   faculty.rb
concerns                student.rb

rails console を使うと、テストやデバッグのために、このファイルを読み込んだ状態で、対話的にRubyを実行することができる。

注釈

日本語が文字化けする場合は、 rails console を実行する前に export LANG=C.UTF-8 を実行しておく。

例えば、総合政策学部のデータを作ってみよう。

# rails console
irb(main):001:0> x = Faculty.new(name: '総合政策')
irb(main):002:0> x.save
irb(main):003:0> x.id
  • Faculty クラスのインスタンスを新しく作り、学部名(カラム名は name )を設定する。

  • name: '総合政策' はキーが :name で値が '総合政策' のハッシュ。

  • できたインスタンスに対して save メソッドを呼び出すと、インスタンスの情報がデータベースに保存される。その時にデータベースを管理するための項目 id, created_at, update_at などが自動的に設定される。 id は、Railsが勝手に1から順につけていく。

データベースに保存されたデータを取り出すには、次のようなメソッドを使う。

  • id が1番の学部のデータが欲しい時は、 Faculty.find(1) を呼び出すと、 Faculty のインスタンスが返ってくる。

  • すべての学部のデータが欲しい時は、 Faculty.all を呼び出すと、 Faculty のインスタンスの配列が返ってくる(正確には ActiveRecord::Relation のインスタンスだけれど、まあ配列と思って使っても大丈夫)。

条件を指定して検索するやり方は次の次の次の週ぐらいにやる。

課題11

科目を表すモデル Course を作り、次のようなデータを登録せよ。ただし、後の都合があるので、 rails generate model を使い、コントローラとビューは作らないこと。

id

name

credit

compulsory

1

体育1

1

true

2

プログラミング言語論

2

false

ターミナルの実行経過をSFC-SFSに貼り付けて提出。

【解答例】

7.5. モデルの対応関係

各モデルのデータの間には、一対一、一対多、多対多の関係がある場合がある。例えば、学部と学生の関係は一対多になる。Railsではそういう関係を宣言しておくと、いろいろと便利になる(Active Record の関連付け)。

例として、総合政策学部の太郎のデータを作ってみよう。総合政策学部の id は1なので、太郎の faculty_id の値は1にする。

irb(main):004:0> y = Student.new(name: '太郎', faculty_id: 1)
irb(main):005:0> y.save

カラムの値は、例えば太郎の学年は y.grade と書ける。太郎が1年生であるとすると、 y.grade = 1 で設定できる。その後に save するのを忘れないように。

irb(main):006:0> y.grade = 1
irb(main):007:0> y.save

太郎の学部を調べたい時は、 faculty_id の値が1なので総合政策学部に所属することがわかる。しかし、 faculty_id の値を見て、その番号の学部を探すというのは面倒なので、学生のインスタンスからすぐに学部のインスタンスが得られるようにしたい。そのためには、学生のクラス app/models/student.rbbelogns_to を追加する。モデル名はシンボルで指定することに注意。

class Student < ApplicationRecord
  belongs_to :faculty
end

すると、 Student クラスに faculty メソッドが(Railsによって勝手に)追加される。これを呼び出すと、その学生に対応する Faculty クラスのインスタンスが得られる。

_images/belongs-to.png

rails console を終了し(コントロールD)、エディタで app/models/student.rb を修正・保存した後、もう一度 rails console を実行する。

# rails console
irb(main):001:0> x = Student.find(1)
irb(main):002:0> x.faculty

逆に、学部のインスタンスからその学部に所属する学生のインスタンス(複数個ある)を得たい時は、 app/models/faculty.rbhas_many を追加する。この場合はモデル名を複数形にする約束になっている(単数形でもたぶん動く)。

class Faculty < ApplicationRecord
  has_many :students
end

そうすると Faculty クラスに students というメソッドが出来て、その学部に所属している学生全員の配列を得ることができる。

# rails console
irb(main):001:0> x = Faculty.find(1)
irb(main):002:0> x.students
_images/has-many.png

課題12

環境情報学部のデータと、花子と一郎は環境情報学部の学生であるということをデータベースに登録し、環境情報学部のインスタンスに対して students メソッドを呼び出すと、二人のデータが出てくることを確認せよ。ターミナルの実行経過をSFC-SFSに貼り付けて提出。

【解答例】

7.6. 今日のまとめ

_images/model-summary.png

ここまでのソースコードはこちら

課題11実行後