Help us understand the problem. What is going on with this article?

Before Rails Tutorial2章 Scaffold を用いた高速なアプリケーション構築 及び MVCの理解

本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用
4章:ログイン機能

1.Scaffold

この章では、Railsの強力な機能をいくつか紹介するためのアプリケーションを作成します。
大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って蔵書管理アプリケーションを生成し、それを元にRailsの概要を一緒にみていきましょう。

*3章以降では、scaffold は使わずに、自力で作る方法を学びます。

scaffold コマンド

最初に、名前と学籍番号を登録するだけの機能を作成します。
User という名前で作成していきます。

下記のコマンドは一字一句間違いなくタイプするか、コピペで行ってください。

失敗した場合は、下記のページに解決策が記載してあります。(すこし複雑です)
scaffold作成時にカラム名(属性名)を打ち間違えた場合の修正手順

terminal
$bin/rails generate scaffold User name:string number:integer

Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。
scaffoldコマンドの引数には、リソース名を単数形にしたもの (この場合はUser) を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します

リソースとは、コントローラが扱う対象に名前をつけたものです。
例:モデル、画像、セッション...etc

自動で多数のファイルが生成されたとおもいます。
重要なのは次のファイルです。

terminal
    create    app/models/user.rb
    create    app/views/users/(略)
    create    app/controllers/users_controller.rb
    route     resources :users

Models Views Controllers の頭文字をとって
MVC と呼びます。

MVC

MVCに関しては3章以降でも学んでいくので、いまはふわっとした理解で大丈夫です。

Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

MVC-02.png

1, ブラウザから /users へのリクエストを Rails sever に送信する
2, router によって、/users は Users controller の index アクションを呼び出す
3, index アクションから、User Model が呼び出される
4, User Model は DataBase からデータを取り出す
5, 取り出したデータを、Controller に返す
6, 受け取ったデータを @users に保存し、 View (indexアクションなので index.html.erb) に渡す。
7, ERBを実行し、@users のデータを含んだ HTMLを生成し、Controller へ返す
8, Controller は、受け取ったHTMLをブラウザにわたす

今後もModel View Controller を構築していくので、適宜図を見返してください。

データベースの利用

このアプリケーションではデータを保存するために、データベースを扱います。
下記コマンドを実行することで、データベースに users という名前のテーブルが作成されます。

terminal
$bin/rails db:migrate

下記のような結果が表示されていればテーブルの生成ができています。
(migrate については今後の章で解説します)

terminal
==  CreateUsers: migrating =============================
-- create_table(:users)
   -> 0.0007s
==  CreateUsers: migrated (0.007s) =============================

コマンド実行後に、結果の文章を読む癖もつけていきましょう
初学者が陥る問題に、terminalにエラー文が表示されているにも関わらず気付かない問題があります。
エラー文を見つけた場合は、エラー文をそのまま検索することで解決策を見つけることも難しくありません。
コマンド実行の結果、どういったことが行われているのかを見ることも理解を深めることに役立ちます。

いま実行した2行で下記の機能が自動的に生成されています。
・ユーザーの作成
・ユーザーの一覧表示
・ユーザーの編集
・ユーザーの削除

試しにサーバーを起動してみましょう。

terminal
$bin/rails server

ブラウザで開いてみましょう。
下記URLにアクセスすると。それに対応した機能が呼び出されます。
一覧表示
 http://localhost:3000/users
新規作成
 http://localhost:3000/users/new
ユーザー表示
 http://localhost:3000/users/1
ユーザー編集
 http://localhost:3000/users/1/edit

たった2行で想像以上の機能が実装されてしまい驚きと困惑があると思います。
多くの初学者が、何が起きたのかわからず Rails を魔法のように感じてしまいます。

自動で作られたファイルについて、簡略的な説明を行っていきます。
3章以降で、いちからつくるので事前に概要を知っておくことは、
今後の理解を深める上で重要です。

ルーティング

まずはどの機能が、どのURLと紐付いているかですが、
下記のコマンドで確認できます。

terminal
$bin/rails routes

下記のような表示がなされていると思います。

GET    /users(.:format)           users#index
POST   /users(.:format)           users#create
GET    /users/new(.:format)       users#new
GET    /users/:id/edit(.:format)  users#edit
GET    /users/:id(.:format)       users#show
PUT    /users/:id(.:format)       users#update
DELETE /users/:id(.:format)       users#destroy

よくみると、show update destroy がすべて /users/:id という同じURLになっています。
同じURLでも機能が異なるのは、利用しているHTTPメソッドが異なるためです。

show : GET データの取得
update : PUTデータの送信(主に更新の際)
destroy : DELETE:データの削除
 HTTPメソッドについて、もっと知りたい方はこちらをご覧ください
 https://tsuyopon.xyz/2019/01/31/understand-4-http-methods/

この紐付け(ルーティング)は、scaffold によって自動的に行われたものですが、
自力で行う場合には config/routes.rb の中に結びつけを書いていきます。
いまは 紐付けは routes.rb で行われている。 とだけ覚えてもらえば大丈夫です。
3章以降で詳しく説明していきます。

紐付けと言われても、すこし伝わりづらいとおもいます。
ルーティングの見方を、例をあげて説明します。
/users に対するルーティングをみると
GET /users(.:format)      users#index
と書かれています。これは usersコントローラーの index アクションを呼び出す
と理解してください。

コントローラー (app/controllers/users_controller.rb) を見てみると下記のコードが書かれています。

app/controllers/users_controller.rb
  def index
    @users = User.all
  end

 Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味です。

 開かれる htmlファイルはどこにあるのでしょう?
 
 実は Rails では、特に指定をしない場合、
 アクション名.html.erb が自動的に呼び出されます。
 重要なので覚えておいてください。
 app/views/users/index.html.erb が呼びされます。

ここまでの流れをまとめると、下記のようになります。

 ①URLにアクセスがあった場合
 ②ルーティングに従ってコントローラーが呼び出され
 ③モデルを経由してデータを受け取り、コントローラーにデータを返し
 ④コントローラーのアクションに従って、データがビューに渡され
 ⑤ビューは受け取ったデータを利用して、HTMLを生成する

上記は、Rails を理解する上で重要な流れなので、覚えておいてください。

MVC(Model View Controller)については後ほど詳しく説明しますが、
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う
と、大まかにご理解ください。

理屈だけより実際に触れてみると理解が深まるとおもうので、
一連のながれを体感するために、View と Controller を触っていきましょう。

2.View

ページの編集

ブラウザから一覧ページを開いてみましょう
http://localhost:3000/users

まずは、メンバー一覧表示ページを編集してみましょう。
編集するファイルは、app/views/users/index.html.erb です。
3行目 h1タグ内の、 Users の部分を下記のように置き換えてみましょう。

app/views/users/index.html.erb
<h1>ユーザーの一覧</h1>

編集が完了したら、上書き保存し、ブラウザを更新してください。
編集が反映されていれば成功です。

erb

ERBとは、Embedded RuBy の略であり、Rubyを埋め込めるHTMLファイルのようなものと
いまは思っておいて頂いて結構です。

ERBは、HTMLテンプレートエンジンと呼ばれ、他にも haml slim などあるので
興味がある方は、調べてみてください。

Ruby の埋め込みも体験してみましょう。
erb 内に下記のコードを記述することで、その中にrubyを埋め込むことができます。

<% %> この中にRubyコードを記述できる。
<%= %> この中に書かれた Ruby は文字列として出力される。

erb:変数の利用

index.html.erb の1行目に下記の2行を追加してください。

app/views/users/index.html.erb
<% name = "Player" %>
<%= "ようこそ #{name} さん" %>

Ruby と同様に、変数の代入と展開が行われ
ようこそ Playerさん と表示されます。

Timeクラスの利用

下記のように追加すると、今日の日付が表示されます。

app/views/users/index.html.erb
<% t = Time.now %>
<%= "いまの時刻は #{t} です" %></br>
<%= "今日は #{t.month}#{t.day} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1)} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1).month}#{t.weeks_ago(1).day} 日です" %>

Timeクラスについて詳しく

【課題1】 erbの変更

5分ほど使って index.html.erb を自由に変更してみてください。
変更例)日本語化
1, name,number をそれぞれ、名前,学籍番号 に変更
2, New user を、新規ユーザー作成 に変更
3, Show,Edit,Destoroy を 表示、編集、削除 に変更

つづいてコントローラーをみていきましょう。

3.Controller

Controller から View への、データの受け渡しを確認

Controllerは、クライアント Model View の仲介を担っています。
Controller名は、複数形にする(命名規則)

再度、コントローラー (app/controllers/users_controller.rb) の下記の行をみてください。

app/controllers/users_controller.rb
  def index
    @users = User.all
  end

 これは、Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味でした。

今回はデータベースを使わずに、連想配列を作成し、擬似的なデータベースを作成してみましょう。
(単純に配列の中にデータをいれているだけです。このあと Model の項目で、データベースを利用するように置き換えます)
@book という連想配列を作成し、その中に情報を書き込んでいきます。

app/controllers/users_controller.rb
  def index
    @users = User.all#←これは自動生成されたもの。
    @book = Hash.new
    @book[:country] = "Japan"
    @book[:title] = "吾輩は猫である"
    @book[:author] = "夏目漱石"
    @book[:year] = 1905
    @book[:book_id] = 1
    @book[:comment] = "おもしろかった"
  end

@bookの中身は次のようになっています
{:country=>"Japan", :title=>"吾輩は猫である", :author=>"夏目漱石", :year=>1905, :book_id=>1, :comment=>"おもしろかった"}
@book[:title] とすると、 "吾輩は猫である" が取り出せます。
Hash(連想配列)をお忘れの方は
Array(配列)とHash(連想配列)入門
を御覧ください。

本の情報を @book 連想配列に追加したので、 View で受け取れるか確認してみましょう。
(def index に記載しているので、 index.html.erb に@bookの情報が送られています)

app/views/users/index.html.erb
<%= @book %></br>
<%= "本のタイトルは #{@book[:title]} です" %></br>
<%= "著者は #{@book[:author]} です" %></br>

http://localhost:3000/users
ブラウザを更新すると下記のように表示されます。

 {:country=>"Japan", :title=>"吾輩は猫である", :author=>"夏目漱石", :year=>1905,
  :book_id=>1, :comment=>"おもしろかった"}
 本のタイトルは 吾輩は猫である です
 著者は 夏目漱石 です

無事、情報を受け取れていますね。

erbの応用

和書かどうかを調べる機能を追加してみましょう。
erb はRubyが使えるので、条件分岐も利用できます

下記のコードを追加してみましょう。

app/views/users/index.html.erb
<% if @book[:country] == "Japan" %>
<p>この本は、和書です</p>
<% end %>

 この本は、和書です
と表示されます。

すこし蔵書管理アプリっぽくなりました。

蔵書データを増やしていきたいところですが、問題があります。
Controller に書くには蔵書データはあまりに多すぎるという問題です。
(そしてユーザーは追記できない)

これを解決するのが、データベースとModelです。

@book に関するコードは、すべて消してしまいましょう。

4.Model

Books リソースの作成

Modelは基本的に、1つのテーブルに対して1つのModelを作成します。
Model名は、複数形にする(命名規則)

人に関するリソースは作成したので、本に関するリソースも作成していきましょう。
(usersテーブルとは、別にbooksテーブルを作成するので、Book Modelを作成します)

再度、Scaffold を実行します。

terminal
$ bin/rails generate scaffold Book country:text title:text author:text year:integer user_id:integer comment:text

Users リソースの時と同様に、自動で多くのファイルが作成されます。
Model View Controller route もそれぞれ作成されています。

terminal
    create    app/models/book.rb
    create    app/views/books/(略)
    create    app/controllers/books_controller.rb
    route     resources :books

前回同様、下記コマンドを実行することで、データベースに books という名前のテーブルが作成されます。

terminal
$ bin/rails db:migrate

ルーティングも確認してみましょう。

terminal
$ bin/rails routes

下記のように、紐付けされていることが確認できます。

GET    /books(.:format)           books#index
POST   /books(.:format)           books#create
GET    /books/new(.:format)       books#new
GET    /books/:id/edit(.:format)  books#edit
GET    /books/:id(.:format)       books#show
PUT    /books/:id(.:format)       books#update
DELETE /books/:id(.:format)       books#destroy

ブラウザで確認してみましょう。本の情報を入力できるようになっています。
http://localhost:3000/books/new

このままではユーザーは自由に投稿できる状態です。
year と user_id は数値を入力してもらいたいので入力制限をかけてみましょう。

Rails では制限(validation)も簡単に設定することができます。

Validation

app/models/book.rb を開き下記のように編集してください。

app/models/book.rb
class Book < ApplicationRecord
  validates :year, :user_id, numericality: true
end

この1行だけで制限を設けることができます。
Book Model に追記しているので、 books テーブルの yearカラム と user_idカラムに対して制限をかけます。
実際に試してみると、下記のようなエラー文が表示されます。
validate.png

【課題2】自作バリデーション

Railsバリデーションまとめ
を参考にして、制限を設けてみましょう。
例)
1, Title を空白禁止にする
2, 著者の名前を125文字以内にする ...etc

バリデーションがうまくかけたかを試すために、
実際にブラウザから本のデータを2件登録してみてください。

内容は自由で良いですが、 user の項目だけは 1 にしてください

rails console

うまく情報が登録されたかを確認するために、データベースから情報を取り出してみましょう。
SQL文が苦手な方もご安心ください。
Rails console を使えば、簡単にデータを取り出してみることができます。

新規ターミナルを起動し、下記のコマンドを実行してください。

terminal
  $bin/rails c

すると >> といった表示がされるとおもいます。これでrails console が起動しました。
( exit 入力し、Enterを実行すると終了します )

試しに、最初に登録したユーザー情報を、(Model を経由し)データベースから取り出してみましょう

railsConsole
  >> user = User.find(1)

user はただの変数です。(名前は自由ですが、わかりやすく use にしました)
User.find(1) は User Model を利用して、 1番目の人を取り出せ
という意味ですので、 U は大文字です。(Model は頭文字が大文字で単数形でしたね)

さきほど、ブラウザから入力したデータが登録されていると思います。

【課題3】 rails c を使った、データの取り出し

本のデータを取り出して表示してみましょう。
その際に、find(1) 意外の取り出し方も試してみましょう。
データ取得メソッドまとめ
例) .all を使用してみる。

MVC総合 User が所持しているデータを表示してみる。

さてここまでで、冒頭に説明した下記の役割を体験してみました。
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

なにもわからなかった状態から、
ルーティング・DB・コンソール・MVC
と多くのことを吸収し、成長を感じられると思います。

まだ大まかな理解だと思うので、更に理解を深めるべく
応用課題に取り組んでいきましょう!

id: 1 のUserが所持している本のデータを取り出してみましょう。

ブラウザでの表示は View を編集する必要があり大変なので
まずは console の中で、取り出しを確認するところだけみていきましょう。
(小さく着実に作っていくことは大事です)

rails console を起動した端末にて下記のコマンドを実行します。

railsConsole
   >> user = User.find(1)
   >> user.books

と入力することで、 id:1 のuserが持っている本のデータ
(booksテーブルの中で、 user_id を 1 にした本のデータ)
がすべて表示されます!

.
..
...
されないとおもいます。
実行結果を見てみると、エラー文が出ていますね

railsConsole
>> user.books
Traceback (most recent call last):
        1: from (irb):2
NoMethodError (undefined method `books' for #<User:0x00007fa63942dce0>)

NoMethodError undefined method `books'
これは、 books がなにかわからない。定義されていない。という意味のエラーです。
(今後良くみかけることになるとおもいます。タイピングミスしてもこのエラーが表示されます)

今回の原因は、 user テーブルと books テーブルが紐付いていないことによるものです。

booksテーブルで勝手に user_id というカラムを追加しただけで、
userテーブルにはなにも書いていないので当然ですね。
Userモデルから、bookテーブルのデータを取得できないのでエラーが出ています
こういったテーブル同士の紐付けも、Railsでは1行で出来ます。

has_many belongs_to

いま、 users と books の2つのテーブルがあります。
どの蔵書データが、どのユーザーに紐付いているのかといった関連付けを行いたいとき
どうすれば良いのかを学んでいきます。

1,一人のuserは、たくさんの本を持っています
こういう場合、 user.rb (UserのModel)に、
has_many :books (たくさんなので、複数形です)
と1行追加するだけで、上記のような紐付けが完了します。

2,1冊の本は、一人のUserに紐付いています
こういう場合、 book.rb (BookのModel)に、
belongs_to :user (ひとつなので、単数形です)
と追加するだけで、上記のような紐付けが完了します。

app/models/user.rb
class Book < ApplicationRecord
  has_many :books
end
app/models/book.rb
class Book < ApplicationRecord
  belongs_to :user
  validates :year, :user_id, numericality: true
end

has_many belongs_to コード追加が終われば
rails console を立ち上げ直してください
(exitとタイプしEnterで終了できます)

再度 rails c を起動し、さきほどと同じコマンドを実行するとどう変わっているでしょうか。

railsConsole
   >> user = User.find(1)
   >> user.books

↓出力結果例

railsConsole
Loading development environment
>> user = User.find(1)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "daichi", number: 10

>> user.books
  Book Load (0.1ms)  SELECT  "books".* FROM "books" WHERE "books"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Book id: 1, country: "Japan", title: "吾輩は猫である", author: "夏目漱石", year: 1905, user_id: 1, comment: "おもしろかった"

紐付けられたことでエラーが解消され、上記のような結果が表示されます。

テーブルに関連性を持たせたいときは has_many belongs_to を使用すること
覚えておいてください。

Modelにて、データベース内のテーブルの関連づけは出来き、
user.books にてユーザーが持っている本のデータを取り出せることが確認できました。

さて、Modelで取り出したデータを、View に送るにはどうすればよかったでしょうか?
そうですね。Controller の役割でしたね。

Controller

app/controllers/users_controller.rb
をひらきます(@bookを消していない人は消しておいてください)

前回は、index.html.erb に情報を渡したかったので index アクションに
@book のデータを(データベースを使わずに直接)書きました。

今回は、show.html.erb に情報を渡したいので、 show アクションに
モデルを用いて、データを取り出すコードを書きます

app/controllers/users_controller.rb
  def show
    @user = User.find(1)
    @books = @user.books
  end

(小文字大文字, s の有無に気をつけてください)
最後に、Controller から送られてきたデータの表示の部分をやっていきます。

View

表示させたい場所は、1番目のユーザーのページなのでこちらです
http://localhost:3000/users/1

【MVCの流れの復習】
ちなみに、ルーティングには下記のように書いてあります
 GET /users/:id       users#show
users/(idの数字) にGETリクエストがあったら users controller の show アクションを実行でしたね。
show アクションが、実行されたら自動的に、 show.html.erb が表示されるのでした。

show.html.erb の一番上に下記コードを追記してください。

app/views/users/show.html.erb
  <% @books.each do |book| %>
    <p><%= "本のタイトルは #{book[:title]} です" %></p>
    <p><%= "著者は #{book[:author]} です" %></p></br>
  <% end %>

これで、 Controller から受け取った @books (本の情報が格納された連想配列)
の中身が表示されていると思います。
(配列の中身を順番に取り出していくときは each でした。 Ruby をお忘れの方は復習を)

【最終課題】

show.html.erb を自由に編集してください

例)
・本の情報をすべて表示

まとめ

以上で、scaffold を用いた、高速蔵書アプリケーションの制作講座は終わりです。
お疲れ様でした。

3章では、scaffold が自動生成していた部分を、1から自分で制作していきます。
今回の内容を再度読み返すか、もう一度素早く作成して復習をしておいてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした