データベースを使ったアプリ作成 4 (Ruby on Rails の利用)
はじめに
今回は TODO リストを管理するアプリを作成する. 以下を修得することを目的とする.
- 既存の実装 (CRUD) を応用し,アクションを追加する
- フォームの送信項目の追加
- 条件に合致するレコードの絞り込み
- 画面の見せ方の変更
scaffold でサンプルの作成
まず,scaffold でアプリを作成する.名前は kadai とする (名前は変更して構わない).
$ cd ~/my_web_apps $ rails new kadai --database=mysql $ cd kadai
今回は JSON 関係は使わないので, Gemfile 内の jbuilder をコメントアウトしておく.
$ vi Gemfile # Build JSON APIs with ease [https://github.com/rails/jbuilder] #gem "jbuilder"
rails new のオプションで --database=mysql を指定したので, MySQL (mariaDB) にアクセスするための情報をファイルに書く.
$ vi config/database.yml default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <-- 入力 password: <-- 入力 socket: /var/run/mysqld/mysqld.sock
scaffold でアプリを作成する.コントローラの名前は todo_list にする. テーブルには title (string 型), content (text 型) の 2 つのカラムを作成する.
$ rails g scaffold todo_list title:string content:text
モデル (データベース) を作成する.
$ rails db:create $ rails db:migrate
サーバを起動する.
$ rails s -b 0.0.0.0
例題
内容
アプリ (/todo_lists/) の見た目を以下のように変更し,全てのページが動作するようにしなさい.ビュー・コントローラ・ルーティングのファイル,CSS ファイル,およびページのスクリーンショット (4 枚,含む URL バー) を提出すること.
- 「インデックスページ」を以下のようにすること
- 各項目が 1 行で示されていること.
- 各項目のタイトルを押すと,「詳細表示ページ」にページ遷移すること.
- 操作のところに「編集」のリンクがあり,それをクリックすると「編集ページ」にページ遷移する.
- 操作のところに「削除」のリンクがあり,それをクリックすると,当該データをデータベースから消去した後に,「インデックスページ」にリダイレクトすること.
- ページの末尾に「追加」のリンクがあり,それをクリックすると「新規作成ページ」にページ遷移すること.
- 「詳細表示ページ」は以下のようにすること.
- タイトルと内容が表示されること.
- ページの末尾に,インデックスページに戻るリンクを配置すること.
- 「編集ページ」は以下のようにすること.
- タイトルと内容を入力できるようにすること.
- ページ末尾の「アップデート」リンクを押すと,データが変更され,「詳細表示ページ」にリダイレクトされること.
- 「新規作成ページ」は以下のようにすること.
- タイトルと内容を入力できるようにすること.
- ページ末尾の「クリエイト」リンクを押すと,データが作成され,「詳細表示ページ」にリダイレクトされること.
考え方
ルーティング
- ルーティングの詳細は以下を参照されたい
scaffold でアプリを作成すると, config/routes.rb には resources :todo_lists だけ書かれている.
$ vi config/routes.rb Rails.application.routes.draw do resources :todo_lists end
この resources メソッドによって 7 つのルーティングが自動で設定される. rails routes コマンドで確認することができる.
$ rails routes | head -n 10 Prefix Verb URI Pattern Controller#Action root GET / todo_lists#index todo_lists GET /todo_lists(.:format) todo_lists#index POST /todo_lists(.:format) todo_lists#create new_todo_list GET /todo_lists/new(.:format) todo_lists#new edit_todo_list GET /todo_lists/:id/edit(.:format) todo_lists#edit todo_list GET /todo_lists/:id(.:format) todo_lists#show PATCH /todo_lists/:id(.:format) todo_lists#update PUT /todo_lists/:id(.:format) todo_lists#update DELETE /todo_lists/:id(.:format) todo_lists#destroy
HTTP のメソッド (GET, POST) などと URL に対応して,コントローラのアクションが決められている. デフォルトでは削除は DELETE メソッドに結びついており,ビューで以下のように button_to で「削除ボタン」を作ると,それは内部で当該 id のデータに対する DELETE メソッドに変換される.結果として,コントローラの destroy アクションが呼ばれてデータが削除される.
<%= button_to '削除', todo_list, method: :delete %> --> DELETE /todo_lists/:id --> todo_lists#destroy
また,以下のようにビューの button_to を link_to に変えただけでは,データの削除はされない. link_to は内部で GET メソッドに変換されるため,結果的にコントローラの show アクションが呼ばれてしまうことになる.
<%= link_to '削除', todo_list, method: :delete %> --> GET /todo_lists/:id --> todo_lists#show
作成 (new_todo_list) や編集 (edit_todo_list) と同様に, GET メソッドで削除を行うためには,config/routes.rb の resources に以下のような追記を行い,削除用のパス (以下の例では delete2_todo_list) を作成する必要がある.
$ vi config/routes.rb Rails.application.routes.draw do resources :todo_lists do get :delete2, on: :member end end
ルーティングを確認すると,以下のように delete2_todo_list (GET メソッド) が作成されていることがわかる.
$ rails routes | grep delete2 delete2_todo_list GET /todo_lists/:id/delete2(.:format) todo_lists#delete2
コントローラ
ルーティングで設定した delete2 アクションをコントローラで設定する必要がある. before_action で新規作成したアクション (delete2) を指定しておかないとビューから利用できない. また,新たに delete2 アクションを定義しておく.なお,delete2 アクションの中身は 既に設定されている destroy とほとんど同じにしている.
$ vi app/controllers/todo_lists_controller.rb 以下を適当な場所に入れる.ActiveRecord の destroy メソッドで当該データを削除する. class TodoListsController < ApplicationController before_action :set_todo_list, only: %i[ show edit update destroy delete2 ] ...(中略)... # GET /todo_lists/1/delete2 def delete2 @todo_list.destroy redirect_to todo_lists_url, notice: "Todo を削除しました" end ...(後略)...
ビュー
インデックスページに「削除」のリンクを作るには,以下のようなソースを 追加すれば良い (scaffold で作られた元々の show.html.erb を参考にすると良い).
<%= link_to '編集', edit_todo_list_path(todo_list)%> <%= link_to '削除', delete2_todo_list_path(todo_list) %>
各ページの装飾には CSS を使う. app/assets/stylesheets/application.css に定義を書けば良い. 先に挙げたページを作るための CSS の一部は以下の通りであるが, 各自好みの見た目となるよう CSS を定義すること (以下と全く同じにしないこと).
$ vi app/assets/stylesheets/application.css table { width:100%; text-align: left; border-collapse: collapse; } th { padding: 10px; border-bottom: solid 5px #748ca5; } td { padding: 10px; border-bottom: solid 1px #748ca5; } div { padding: 0.5em 1em; margin: 2em 0; font-weight: bold; color: #6091d3;/*文字色*/ background: #FFF; border: solid 3px #6091d3;/*線*/ border-radius: 10px;/*角の丸み*/ } ...(略)...
課題
内容
例題で完成させたアプリをベースに,TODO の期日や完了の機能を実装する.
アプリ (/todo_lists/) の見た目を以下のように変更し,全てのページが動作するようにしなさい.ビュー・コントローラ・ルーティングのファイル,およびページのスクリーンショット (4 枚,含む URL バー) を提出すること.
- データベースに,終了したか否かを示すカラムと期日のカラムを追加する.
- カラム名は,それぞれ completed と deadline とする.
- completed の型は boolean, deadline の型は date とする.
- カラム名は,それぞれ completed と deadline とする.
- 「インデックスページ」を以下のようにすること
- まだ完了していないもの (todo_lists テーブルのカラム completed が nil や空白なもの) だけを表示する.
- 表中に期日 (todo_lists テーブルのカラム deadline) も表示する.
- 操作のところに「完了」のリンクを追加する.それをクリックすると,データベースの completed カラムに 1 ないし true が入力され,「インデックスページ」にリダイレクトされること.
- 完了操作を行った項目がインデックスページで表示されなくなることを確認すること.
- TODO の各項目のうち,期日まで 7 日以内のものについて背景を「緑」にする.
- TODO の各項目のうち,期日を過ぎたものについて背景を「赤」にする.
- 期日が古いものから新しいものとなるよう,データをソートして並べること.
- 「詳細表示ページ」は以下のような見た目にすること.
- 期日を表示するようにすること.
- 「編集ページ」は以下のような見た目にすること.
- 期日を設定できるようにすること.
- 「新規作成ページ」は以下のような見た目にすること.
- 期日を設定できるようにすること.
- 自分なりの工夫をこらした場合は,評価点を増やします.
考え方
モデル
データベースの todo_lists テーブルにカラムを追加する必要がある. 追加の仕方は,データベースを使ったアプリ作成 2 を参照すること.
$ rails g migration AddCompletedToTodo_lists completed:boolean $ rails g migration AddDeadlineToTodo_lists deadline:date $ rails db:migrate
ルーティング
例題の delete2 を参考に,config/routes.rb に complete へのルートを設定する.
resources :todo_lists do get :complete, on: :member get :delete2, on: :member end
コントローラ
コントローラ (app/controllers/todo_lists_controller.rb) に Todo を完了するためのアクション (complete) を実装する.
before_action や StrongParameter の設定も忘れないこと.
before_action :set_todo_list, only: %i[ show edit update destroy delete2 complete ] ... # GET /todo_lists def index # @todo_lists = TodoList.all # これは全件検索 @todo_lists = TodoList.where(completed: [nil, '']).order(:deadline) # 検索条件付けて,降順に並べる end # ActiveRecord の書式 ... # GET /todo_lists/1/complete def complete @todo_list.update(completed: true) # データベースのカラムの値をアップデート redirect_to todo_lists_url, notice: "Todo を完了しました" # ActiveRecord の書式 end ... def todo_list_params params.require(:todo_list).permit(:title, :content, :deadline) end
ビュー
期日までの日数に応じて背景色を変える操作はビューで行えば良い. Ruby で現在時刻を取得し (Date.current),その値と todo_list.deadline の値を比較する.
<tbody> <% @todo_lists.each do |todo_list| %> <% current_date = Date.current if todo_list.deadline if (todo_list.deadline <= current_date) bgcolor = "pink" elsif (todo_list.deadline <= current_date + 7) bgcolor = "lightgreen" else bgcolor = "white" end end %> <tr bgcolor=<%=bgcolor%>> ...(略)...
作成や編集画面で期日を設定するためには,form.date_field を使うと良い.
<%= form.label :deadline, style: "display: block" %> <%= form.date_field :deadline %>