#ユーザーのモデルを作成する
新しいユーザーを作成するためのスタブページを作ったところで終わりました
これから6つの章を通して、ユーザー登録ページを作っていく
ことにしましょう。
本章では、
一番重要なステップであるユーザー用のデータモデルの作成
と、
データを保存する手段の確保
について学んでいきます。
######自分で認証システムを作ってみる
すべてのWebアプリケーションは何らかのログイン/認証システム
を必要と
多くのWebフレームワークではこのようなログイン/認証システムを実装するための選択肢が多数提供されています
。
出来合いのシステムは内部が謎だらけの“ブラックボックス”になっている可能性もあり、
そうしたシステムで使われている複雑怪奇なデータモデルをいきなり用いると初心者はもちろん、
ベテラン開発者すらそうしたモデルに不慣れであれば、途方に暮れてしまうかもしれません。
自分自身で認証システムを構築した経験があれば
、サードパーティ製品を理解し、必要に応じて変更することがずっと容易になるはずです。
##Userモデル
ここから3つの章にわたる最終目標はユーザー登録ページを作成すること
今のままでは新しいユーザーの情報を受け取っても保存する場所がない
ので、いきなりページを作成するわけにはいきません。
ユーザー登録でまず初めにやることは、それらの情報を保存するためのデータ構造を作成
することです。
Railsでは、データモデルとして扱うデフォルトのデータ構造
のことをモデル(Model)と呼びます
Railsでは、データを永続化
するデフォルトの解決策として、データベースを使ってデータを長期間保存
します。
データベースとやりとりをするデフォルトのRailsライブラリ
はActive Recordと呼ばれます
Active Recordは、データオブジェクトの作成/保存/検索のためのメソッド
を持っています。
Railsにはマイグレーション(Migration)
という機能があります。
データの定義をRubyで記述することができ、SQLのDDL(Data Definition Language)を新たに学ぶ必要がありません。つまりRailsは、データベースの細部をほぼ完全に隠蔽し、切り離してくれます。
$ git checkout -b modeling-users
###データベースの移行
カスタムビルドクラスのUserを思い出してください
このクラスは、nameとemailを属性に持つユーザーオブジェクトでした
Railsにとって極めて重要な部分である永続性という要素が欠けていました。
RailsコンソールでUserクラスのオブジェクトを作っても、コンソールからexitするとそのオブジェクトはすぐに消えてしまいました。
この節での目的は、簡単に消えることのないユーザーのモデルを構築
することです。
nameとemailの2つの属性からなるユーザーをモデリングするところから始めましょう。
class User
attr_accessor :name, :email
#インスタンス変数の読み取りの確認
.
.
.
end
Railsはデータを保存する際にデフォルトでリレーショナルデータベース
を使います。
リレーショナルデータベースは、データ行で構成されるテーブルからなり、各行はデータ属性のカラム(列)
を持ちます。
nameやemailといったカラム名を今のうちに考えておくことで、後ほどUserオブジェクトの各属性をActiveRecordに伝えるときに楽になります。
ユーザーコントローラ(とnewアクション)を作ったときに使った次のコマンドを思い出してみてください
$ rails generate controller Users new
モデルを作成するときは、上と似たようなパターンでgenerate model
というコマンドを使います。
今回はnameやemailといった属性を付けたUserモデルを使いたいので、実際に打つコマンドは下
####Userモデルを生成する
ubuntu:~/environment/sample_app (modeling-users) $ rails generate model User name:string email:string
Running via Spring preloader in process 4242
invoke active_record
create db/migrate/#####_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
コントローラ名には複数形を使い、モデル名には単数形を用いるという慣習を頭に入れておいてください。コントローラはUsersでモデルはUserです。
name:stringやemail:stringオプションのパラメータを渡すことによって、データベースで使いたい2つの属性をRailsに伝えます
これらの属性の型情報も一緒に渡します
アクション名を使って生成した例と比較してみてください。
rails generate controller StaticPages home help
generateコマンドの結果のひとつとして、マイグレーションと呼ばれる新しいファイル
が生成されます。
マイグレーションは、データベースの構造をインクリメンタルに変更する手段を提供します。それにより、要求が変更された場合にデータモデルを適合させることができます
マイグレーションはモデル生成スクリプトによって自動的に作られました。リスト 6.2に示したようにnameとemailの2つのカラムを持つusersテーブルを作成します
####(usersテーブルを作るための)Userモデルのマイグレーション
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
マイグレーションファイル名の先頭には、それが生成された時間のタイムスタンプが追加されます
マイグレーション自体は、データベースに与える変更を定義したchangeメソッド
の集まりです
changeメソッドはcreate_table
というRailsのメソッドを呼び、ユーザーを保存するためのテーブルをデータベースに作成します。
create_tableメソッドはブロック変数
を1つ持つブロックを受け取ります。
そのブロックの中でcreate_tableメソッドはtオブジェクトを使って、nameとemailカラムをデータベースに作ります
。型はどちらもstringです4 。
ロックの最後の行t.timestamps
は特別なコマンドで、created_at
とupdated_at
という2つの「マジックカラム(Magic Columns)」を作成します。
これらは、あるユーザーが作成または更新されたときに、その時刻を自動的に記録するタイムスタンプです
マイグレーションは、次のようにdb:migrateコマンド を使って実行することができます。これを「マイグレーションの適用(migrating up)
$ rails db:migrate
初めてdb:migrateが実行されると、db/development.sqlite3という名前のファイルが生成されます。
###演習
1.あなたの環境にあるdb/schema.rbの内容を調べ、その内容とマイグレーションファイル(リスト 6.2)の内容を比べてみてください。
#####db/schema.rb
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 0) do
end
#####マイグレーションファイル(リスト 6.2)
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
2つのコードはブロックみたいに見える。
2.ほぼすべてのマイグレーションは、元に戻すことが可能です
元に戻すことを「ロールバック(rollback)と呼び、Railsではdb:rollback
というコマンドで実現できます。
$ rails db:rollback
上のコマンドを実行後、db/schema.rbの内容を調べてみて、ロールバックが成功したかどうか確認してみてください。
上のコマンドでは、データベースからusersテーブルを削除するためにdrop_tableコマンドを内部で呼び出しています。
これがうまくいくのは、drop_tableとcreate_tableがそれぞれ対応していることをchangeメソッドが知っているからです。
buntu:~/environment/sample_app (modeling-users) $ rails t
Running via Spring preloader in process 10489
Migrations are pending. To resolve this issue, run:
rails db:migrate RAILS_ENV=test
ubuntu:~/environment/sample_app (modeling-users) $ rails db:migrate
== ####### CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0054s
== ####### CreateUsers: migrated (0.0064s) =============================
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: #########) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
変化が起きた。内容はわからない。
ubuntu:~/environment/sample_app (modeling-users) $ rails db:rollback
== ########## CreateUsers: reverting ======================================
-- drop_table(:users)
-> 0.0062s
== ########## CreateUsers: reverted (0.0132s) =============================
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 0) do
end
元に戻った。
3.もう一度rails db:migrateコマンドを実行し、db/schema.rbの内容が元に戻ったことを確認してください。
ubuntu:~/environment/sample_app (modeling-users) $ rails db:migrate
== ######## CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0299s
== ######## CreateUsers: migrated (0.0311s) =============================
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `rails
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: #########) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
できた。