本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用
4章:ログイン機能
前章では、Scaffoldを用いて高速でアプリケーションを構築しました。
自動化された部分も多く、現段階では、大まかな理解をしていると思います。
今回は、route, Model, View, Controller をそれぞれ自分で作っていきます。
とはいっても、継承まで含めてすべて書くには理解することが膨大で大変ですので、
前回も使用したgenerate
を今回も使用していきます。
まずは、Railsの新しいアプリケーションを作成します。
2章:環境構築の内容を参考に作成してみましょう。
(rails_app を削除していた場合は、 mkdir rails_app とすれば作成可能です)
$ cd ~/rails_app
$ mkdir bukukore
$ cd bukukore
$ bundle init
(Gemfileに gem "rails","~> 5.2.3"を追加し保存してください)
$ bundle install --path vendor/bundle
$ bundle exec rails new .
$ bundle update
$ bin/rails s
http://localhost:3000/
にアクセスして表示されていればokです。
Ctrl キー + C キー 同時押しでrailsサーバーをシャットダウンできます。
シャットダウンせずにサーバー起動したまま開発を続けるにはターミナルを別途立ち上げ
開発フォルダに移動(cd bukukore 等)すればコマンドを実行していけます。
$ rails s -p 3100 などとポートを変更することでサーバーを複数起動可能です。
この場合は http://localhost:3100/ にアクセスします。
MVC 復習
前章では、アプリ制作を通して、MVCの流れを理解してきました。
Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う
前回制作した流れを図を見ながら復習していきます。
それぞれの番号が、コードではどうなっていたか思い出しながら見てください。
- ブラウザから /users へのリクエストを Rails sever に送信する
- router によって、/users は Users controller の index アクションを呼び出す
- index アクションから、User Model が呼び出される
- User Model は DataBase からデータを取り出す
- 取り出したデータを、Controller に返す
- 受け取ったデータを @users に保存し、 View (indexアクションなので index.html.erb) に渡す。
- ERBを実行し、@users のデータを含んだ HTMLを生成し、Controller へ返す
- Controller は、受け取ったHTMLをブラウザにわたす
今回も Model, View, Controller を構築していくので、適宜図を見返してください。
Controller と View の作成
作成は基本的に generate コマンドを用います。
(gは generateコマンドの短縮形です)
g の後は、controller に続けて、コントローラーの名前(複数形
)を書きます。
rails g controller Books index show new
そのうしろ(引数といいます)に書いたもの(index,show,new)は、
同名のviewとアクションが自動生成されます。
(Viewは views/books の中に、アクションは books_controllerの中に生成)
$ bin/rails g controller Books index show new
create app/controllers/books_controller.rb
route get 'books/index'
get 'books/show'
get 'books/new'
create app/views/books
create app/views/books/index.html.erb
create app/views/books/show.html.erb
create app/views/books/new.html.erb
ルーティングが生成されているので、確認してみましょう。
$ bin/rails routes
Verb URI Pattern
GET /books/index books#index
GET /books/show books#show
GET /books/new books#new
これは具体的には、 http://localhost:3000/books/index
というURLに対するリクエスト1があれば、 books_controller の index アクション を呼び出すというものでした。
今回indexアクションの中身が空なので、Model は呼び出されず、単に対応するView (index.html.erb) が実行されるだけです。(図の3,4,5の部分が素通りされます)
ルーティングがうまくいってみるか確認するために、試しにアクセスしてみましょう。
(サーバーを起動させるときは、 $ rails s をタイプします)
http://localhost:3000/books/index
Books#index
Find me in app/views/books/index.html.erb
と表示されているとおもいます。
####Viewの変更
日本語にしておきましょう。erbの変更は前回行ったとおりですね。とてもシンプルなものでした。
Books#index の部分を、全蔵書一覧などに変更しておきましょう。
<h1>全蔵書一覧</h1>
erb の中身がシンプルすぎて不思議な方もいらっしゃるかもしれません。
【気になって次にすすめない人向けの解説】
app/views/layouts/application.html.erb を開いてください。
コード内の、<%= yield %> の部分に、 index.html.erb が移植されて HTML を生成しています。
いまはこの程度の理解で問題ありません。 https://railstutorial.jp/ でより深く理解できます。
####Controllerの変更
コントローラーの中にアクションが本当に作られているかも確認してみましょう。
app/controllers/books_controller.rb を開いてみてください。
class BooksController < ApplicationController
def index
end
def show
end
def new
end
end
データベース内のデータを取り出すには、Controllerのアクションから
Model を呼び出せば良いのでした。
class BooksController < ApplicationController
def index
end
def show
@book = Book.find(1)
end
def new
end
end
このままではエラーがでます。(Modelも、本の情報も作っていないので)
generate コマンドを使用して、Model を作成していきます。
#Model の作成
booksテーブルには、前章と同じカラム(country,title,author,year,user_id,comment)を持たせていきます。
$ bin/rails g model Book country:text title:text author:text year:integer user_id:integer comment:text
create db/migrate/20200708070148_create_books.rb
create app/models/book.rb
g の後は、model に続けて、Model名(単数形
)を書きます。
データベースにテーブルを作成するには migrate を実行するのでしたね。
$ bin/rails db:migrate
== 20200708070148 CreateBooks: migrating ======================================
-- create_table(:books)
-> 0.0008s
== 20200708070148 CreateBooks: migrated (0.0009s) =============================
補足. migrate はマイグレーションファイルを実行しています。
db/migrate/(作成日時)_create_books.rb というファイルです。
ひらいてみると、下記のようなコードがあると思います。class CreateBooks < ActiveRecord
def change
create_table :books do |t|
t.text :country
t.text :title
t.text :author
t.integer :year
t.integer :user_id
t.text :comment
t.timestamps
end
end
end上記は text型であるcountryというカラム(他6つ)を持つ books テーブルを作成
せよという意味です。 change 意外に up down などもあります。詳細は下記サイトなどを参照ください
https://www.sejuku.net/blog/14229
これでbooksテーブルが作成されました。
テーブルは作成したけども、本の情報はまだなにもいれてませんね。
初期データをいれるには db/seeds.rb2 ファイルを使用します。
( seed = 種 という意味です )
大量のデータを入力するのは手間なのでseed.rbを作成していきます。
(そもそもブラウザからデータをいれる機能もまだ作っていません)
下記の seed ファイルを実行すると、bookテーブルに本の情報が3件保存されます
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save
@book = Book.new
@book.country = "jp"
@book.title = "人間失格"
@book.author = "太宰治"
@book.year = 1948
@book.user_id = 2
@book.comment = "すばらしかった"
@book.save
@book = Book.new
@book.country = "gb"
@book.title = "Alice's Adventures in Wonderland"
@book.author = "Lewis Carroll"
@book.year = 1865
@book.user_id = 1
@book.comment = "Amazing"
@book.save
seed ファイルは、下記のコマンドで反映できます。
$ bundle exec rake db:seed
データが保存されたかの確認はrails c
でコンソールを起動すると確認できます。
$ rails c
irb >> book = Book.find(1)
irb >> book
ブラウザ(show)で表示してみましょう。
<p>本のタイトル: <%= @book.title %></p>
<p>著者: <%= @book.author %></p>
本のタイトル: 吾輩は猫である
著者: 夏目漱石
と表示されていればOKです。
####課題1
@book はどこで定義しましたか。
URLからパラメーターの取得
本のデータを取り出して表示するところまでうまくいきました!
すごい進歩です!
ただ、まだ不満点があります。
それは、 id:1 の本のデータしか表示できていない点です。
理由は、Contoroller の show アクションをみると、
@book = Book.find(1)
となっているためです(id:1 の本の情報しか見つけてきていない)
ここをidパラメーターが1以外も入力できるように変更するにはどうすればよいでしょうか。
@book = Book.find(params[:id])3
とすることで、1以外のパラメーターを見つけてこれます。
def show
@book = Book.find(params[:id])
end
パラメーター(数値)は、どこで指定すればよいでしょうか。
URL にパラメーターを含めて渡すテクニックがあるので覚えておきましょう。
例)ID:2の本の情報を取得したい場合 http://localhost:3000/books/show/2
このままではルーティングエラーが表示されます。
books/show へのリクエストに関しては記述していますが、
books/show/(数字) へのリクエストについてはなにも書いていない為です。
config/routes.rb を開いてください。
URLに含まれる数字をパラメーターとして取得するには、下記のように記述します。
# get 'books/show'(# でコメントアウトしてます)
get 'books/show/:id', to:'books#show'
こうすることで /books/show/2 の 2の部分が、:id パラメーターとして利用可能になります。
( 利用するときは、params[:id] とかきます)
to: 'books#show'
booksコントローラーの show アクションを呼び出す指示になります。
これで、任意のidの本情報を取得できるようになりました。
http://localhost:3000/books/show/1
http://localhost:3000/books/show/2
Model 検索
いま本の情報の取り出しに id を利用していますが、
id 以外のカラムを用いて取り出す方法も覚えておくと便利です。
コンソールを起動してください。 ( rails c )
下記を参考に色々と試してみてください。
>> Book.all
#bookテーブルのすべてのデータを取得します。
>> Book.find_by(title: "人間失格")
#条件に合ったデータを、1件だけ取得します。
>> Book.where(country: "jp")
#条件に合ったデータを、すべて取得します。
>> Book.where(country: "jp").where(year: 1905)
#AND検索。 和書で 1905年出版の本を取得しています。
>> Book.where("(country = ?) OR (year = ?)","jp",1905)
#OR検索。 和書 または 1905年出版の本を取得しています。
>> Book.order(create_at: :asc)
#並び順を指定します。 作成日時の昇順で取得しています。
>> Book.where(user_id: 1).order(id: :desc)
#user_id 1さんの蔵書を、idの降順で取得しています。
where の利用
検索条件を学んだのでこれ使って、蔵書管理アプリケーションらしく
個人の蔵書を一覧表示する機能を構築してみましょう。
def show
# @book = Book.find(params[:id]) コメントアウトしました。
@book = Book.where(user_id: params[:id])
end
user_id が パラメーター番号の人の蔵書データをすべて取得して@bookに格納しています。
View も編集していきましょう。 @book のなかに複数のデータがある場合(連想配列)
each を用いて、順に表示させていくのでしたね。(2章の復習です)
<% @book.each do |book| %>
<p>本のタイトル: <%= book.title %></p>
<p>著者: <%= book.author %></p>
<% end %>
http://localhost:3000/books/show/1
にアクセスしてみると、 user_id:1 になっている本のデータがすべて表示されました。
本のタイトル: 吾輩は猫である
著者: 夏目漱石
本のタイトル: Alice's Adventures in Wonderland
著者: Lewis Carroll
【補足】「あれ? has_many とかつけなくてよいんだっけ?」
と思った方は、前章の内容を覚えてくれていますね。
ただ少し勘違いがあります。 has_many,belongs_to は異なるテーブルの紐付けに使います。
今回は、 Book Modelを経由し、Bookテーブルだけにアクセスしているので不要です。
User Model から、Bookテーブルのデータを取り出すならば必要になってきます。
データ登録フォームの作成
さて、データベースから情報を取り出したり加工したりする方法は覚えました。
しかしこのままではユーザーは、本を登録することができません。
本を登録するための機能を作成する方法を学んでいきましょう。
登録形式は、フォーム。
URL は http://localhost:3000/books/new
とします。
form_for4
データベースに新しいデータを保存したいので、 Model を呼び出しましょう。
場所はどこに書きましょう。
new.html.erb に登録フォームを置くので、 new アクションの中ですね。
新しいデータを登録するときは、 Model名.new とします。下記のコードを追記してください。
def new
@book = Book.new
end
new.html.erb に 入力フォームを作成していきましょう。
rails では、フォームを簡単につくれる form_forヘルパーというものがあります。
<%= form_for(@book) do |f| %>
<%= f.label :country %>
<%= f.text_field :country %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :author %>
<%= f.text_field :author %>
<%= f.label :year %>
<%= f.number_field :year %>
<%= f.label :user_id %>
<%= f.number_field :user_id %>
<%= f.label :comment %>
<%= f.text_area :comment %>
<%= f.submit "送信" %>
<% end %>
form_for に関する詳しい使い方は、こちらのサイトで学習してください。
form_forの使い方を徹底解説!
ここでは重要な部分だけ、簡単に解説します。
上の form_forヘルパーにより、下記のような HTML が生成されます。(少しシンプル化してます)
<form class="new_book" id="new_book" action="/books" method="post">
<label for="book_country">Country</label>
<input type="text" name="book[country]" id="book_country" />
<label for="book_title">Title</label>
<input type="text" name="book[title]" id="book_title" />
<label for="book_author">Author</label>
<input type="text" name="book[author]" id="book_author" />
<label for="book_year">Year</label>
<input type="number" name="book[year]" id="book_year" />
<label for="book_user_id">User</label>
<input type="number" name="book[user_id]" id="book_user_id" />
<label for="book_comment">Comment</label>
<textarea name="book[comment]" id="book_comment"> </textarea>
<input type="submit" name="commit" value="送信" />
</form>
ここで重要な属性は、
<form class="new_book" id="new_book" action="/books" method="post">
の中の、 action="/books" と method="post" の2つです。
この2つは送信ボタンが押されたら、
/books に対して、 POST リクエストを送信する
という指示をしています。
なので、 /books 対して、 POST リクエストがあった場合の処理を書いていきましょう。
送信したあとは、books Controller の create アクションに飛ばしたいです。
(のちほどデータベースに保存する処理を create アクションに書いていきます)
post 'books', to: 'books#create'
コードはこのようになるので、 routes.rb に追記しましょう。
(ルーティング関係は、 routes.rb でしたね)
Rails.application.routes.draw do
get 'books/index'
get 'books/show/:id', to: 'books#show'
get 'books/new'
post 'books', to: 'books#create'
end
index と new は to: が書かれていませんね。
前章でも説明したとおり、Railsは、アクションが呼び出されると、
自動的に アクション名.html.erbが呼び出されるので、
下記と同じ意味になります。
get 'books/index' to: 'books#index'
get 'books/new', to: 'books#new'
1,登録ボタンが押される
2,フォームに入力されたデータを、create アクションに飛ばす
という流れで、データが動いているので最後に、create アクションの中で情報をデータベースに保存する処理を書きます。Modelを呼び出します。
書き込みの仕方は、seed.rb でもやりましたね。
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save
【備考】
ちなみに、このままなにも受け取り処理を書かずに情報を送信してみるとどうなるでしょうか。
試しに、本の情報をフォームに入力し送信してみると、なにも動きません。
送信しているので、フォームから パラメーターは飛んでいます。
このパラメーターですが、実は見ることが出来るんです。
rails s を起動している、 terminalをみてください。
いつの間にか、いろんな文字が出力されていますね。
サーバーが受け取ったデータなどの情報が表示されています。
この先、困ったときにここをみると解決できることがあると思います。
"どういったデータがどういう形式で動いているかは、サーバーのログをみる"
自分でサービスをつくるのに、重要なテクニックなので覚えておいてください
サーバーログの一番下をみてみましょう。(下図は、ややシンプル化しています)
Started POST "/books"
Processing by BooksController#create as HTML
Parameters: {"book"=>{"country"=>"jp", "title"=>"吾輩は猫である", "author"=>"夏目漱石",
"year"=>"1905", "user_id"=>"1", "comment"=>"おもしろかった"}}
パラメーターをみると、二重の連想配列になっているのがわかると思います。
( parameters{} の中に book{} が入っている )
通常の連想配列の場合は、params[:title] として取得できていました。
二重の連想配列の場合は、params[:book][:title] とすることで取得できます。
( bookキー の中のtitleキー のデータを取得しています )
連想配列について詳しくはこちらを参照してください Array(配列)とHash(連想配列)入門
さて、ここまで理解できていれば create アクションの中身も想像できると思います。
( seed.rb の、本の情報が書かれていた部分をパラメーターの中身に書き換えるだけです )
def create
@book = Book.new
@book.country = params[:book][:country]
@book.title = params[:book][:title]
@book.author = params[:book][:author]
@book.year = params[:book][:year]
@book.user_id = params[:book][:user_id]
@book.comment = params[:book][:comment]
@book.save
redirect_to '/books/show/1'
end
最後の、redirect_to '/books/show/1' の部分ですが、
登録ボタンを押したあとに、画面遷移してほしいので、リダイレクト先を指定しました。
http://localhost:3000/books/new ここで本の情報を登録
http://localhost:3000/books/show/1 ここで登録内容を確認
(Userの欄を、1以外にした人は、該当ユーザーのページを開いてください)
##課題
1, show ページに、タイトルと著者以外の情報も表示してください。
2, リダイレクト先を、 http://localhost:3000/books/index に変更してください。
3, index ページに、全蔵書のタイトルを表示させてださい。
(全ての本のタイトルを一覧表示してください)
1. コンピュータシステム上で一方から他方へ送信される、何らかの要求、あるいは、メッセージのこと
2. 初期データを生成してくれるファイルのこと。このファイルにデータを生成するコードを書いておくだけで、アプリにデータを備えさせることができる。
3. 主キーに対応するレコードを取り出すことができる。
4. railsで情報を送信するためのヘルパーメソッド。form_forを使うことにより、簡単に入力フォームに必要なHTMLを作成することができる