1. 目的
- CRUD(Create/Read/Update/Delete) の 7アクション(index, show, new, create, edit, update, destroy) を一通り体験する。
- resources が作るルートとパスヘルパ、HTTPメソッド(GET/POST/PATCH/PUT/DELETE) の役割を、テーブル操作の観点で説明できる。
- Controller での
.all
/.new
/.find
/.save
/.update
/.destroy
の使い分けを身につける。
2. ファイル/フォルダ作成
コマンドプロンプト
rails g model l07/tweet title:string body:text
rails g controller l07/tweets index show new edit
- これで以下が生成されます:
db/migrate/*_create_l07_tweets.rb
app/models/l07/tweet.rb
app/controllers/l07/tweets_controller.rb
app/views/l07/tweets/{index,show,new,edit}.html.erb
3. 各ページのつながり(resources/HTTPメソッド/routesの読み方)
3-1. resources が作る7ルート
config/routes.rb
Rails.application.routes.draw do
namespace :l07 do
resources :tweets # ← これ1行で7アクション分のRESTルート
end
end
パスを確認してみよう!
コマンドプロンプト
rails routes -g tweets
パス | メソッド | URL | Controller#Action | 使うパスヘルパ(例) |
---|---|---|---|---|
tweets | GET | /tweets | tweets#index |
tweets_path (一覧) |
tweets | POST | /tweets | tweets#create |
tweets_path (新規作成の送信先) |
new_tweet | GET | /tweets/new | tweets#new |
new_tweet_path (新規フォーム) |
edit_tweet | GET | /tweets/:id/edit | tweets#edit |
edit_tweet_path(@tweet) (編集フォーム) |
tweet | GET | /tweets/:id | tweets#show |
tweet_path(@tweet) (詳細) |
tweet | PATCH/PUT | /tweets/:id | tweets#update |
tweet_path(@tweet) (更新の送信先) |
tweet | DELETE | /tweets/:id | tweets#destroy |
tweet_path(@tweet) (削除の送信先) |
3-2. HTTPメソッドの重要性と機能
-
GET:見るだけ
-
使う画面: 一覧・詳細・新規フォーム・編集フォーム(
index/show/new/edit
) - テーブル: 変化なし(行は増えない・消えない・書き換わらない)
-
使う画面: 一覧・詳細・新規フォーム・編集フォーム(
-
POST:新しく作る
-
使う場面: 新規作成(
create
、POST /tweets
) -
テーブル: 1行追加(
title
,body
などの列に値が入り、新しいIDの行ができる)
-
使う場面: 新規作成(
-
PATCH:一部を直す
-
使う場面: 既存の一部変更(
update
、PATCH /tweets/:id
) - テーブル: 既存1行の一部の列だけが書き換わる
-
使う場面: 既存の一部変更(
-
PUT:上書きするイメージ
-
使う場面: API等で全置換的に更新(RailsではPATCHと同じ
update
に届く) - テーブル: 既存1行が新内容で更新
-
使う場面: API等で全置換的に更新(RailsではPATCHと同じ
-
DELETE:消す
-
使う場面: 削除(
destroy
、DELETE /tweets/:id
) - テーブル: 1行削除(関連設定により子も消えることあり)
-
使う場面: 削除(
4. 解説
4-1. routes.rb
config/routes.rb
Rails.application.routes.draw do # ルーティング定義の開始
namespace :l07 do # /l07 プレフィックス+L07:: 名前空間
resources :tweets # 7アクションRESTルートを一括定義
end # namespace 終了
end # draw 終了
コードの解説
-
namespace :l07
- URLを
/l07/...
にまとめ、対応コントローラはL07::
名前空間で探す。
- URLを
-
resources :tweets
-
tweets
の 7アクションのルートとパスヘルパを一括生成。
-
4-2. tweet.rb
app/models/l07/tweet.rb
class L07::Tweet < ApplicationRecord # L07::Tweet モデル(物理テーブル l07_tweets と対応)
self.table_name = "l07_tweets" # 名前空間つきなので物理名を明示
# バリデーション等は今回は未設定
end
コードの解説
-
self.table_name = "l07_tweets"
- 名前空間付きでも 物理テーブル名を明示して混乱を防止。
4-3. tweets_controller.rb
app/controllers/l07/tweets_controller.rb
class L07::TweetsController < ApplicationController
def index
tweets = L07::Tweet.all
end
def new
@tweet = L07::Tweet.new
end
def create
tweet = L07::Tweet.new(tweet_params)
if tweet.save
redirect_to :action => "index"
else
redirect_to :action => "new"
end
end
def show
@tweet = L07::Tweet.find(params[:id])
end
def edit
@tweet = L07::Tweet.find(params[:id])
end
def update
tweet = L07::Tweet.find(params[:id])
if tweet.update(tweet_params)
redirect_to :action => "show", :id => tweet.id
else
redirect_to :action => "new"
end
end
def destroy
tweet = L07::Tweet.find(params[:id])
tweet.destroy
redirect_to action: :index
end
private
def tweet_params
params.require(:l07_tweet).permit(:body)
end
end
コードの解説
-
def
とは-
def ... end
- 処理のかたまりに名前を付ける。これがメソッド。
-
戻り値
- 最後に記述された式がそのまま返る(return は省略可)。
-
-
def index
-
Tweet.all
-
テーブル
l07_tweets
の全行をまとめて扱う“一覧(コレクション)” を返します。 - ビュー側で
@tweets.each
のように ループして使います。
-
テーブル
-
-
def new
-
Tweet.new
- 仮のレコード(空) を作ります。
- DBにはまだ書き込まれないので、フォーム表示の初期値として使います(
new
アクション)。
-
-
def create
-
Tweet.new(tweet_params)
(引数あり)- 受け取った値をまとめて
tweet_params
メソッドにセットして、仮のレコードを作ります。 - まだDBには書かれません。保存は
.save
のタイミングです。
- 受け取った値をまとめて
-
tweet.save
- 仮のレコードを DBにINSERT(1行追加) します。
- 成功なら
true
、失敗ならfalse
を返します(失敗時はtweet.errors
に理由が入ります)。
-
redirect_to :action => "..."
- 指定アクションへ 別リクエストとして移動(リダイレクト) します。
- 近年は
redirect_to l07_tweets_path
のように パスヘルパを使う書き方も一般的です。
-
-
def show/edit/update/destroy
-
.find(params[:id])
- URLの
:id
を使って 既存の1行を取得 します(主キー検索)。 - 見つからない場合は 例外(404相当) が発生します。
show/edit/update/destroy
で使います。
- URLの
-
-
def update
-
tweet.update(tweet_params)
- 既存行の属性を まとめて上書きして保存(UPDATE) します。
- 成功で
true
、失敗でfalse
を返します(失敗時はtweet.errors
を確認)。
-
-
def destroy
-
tweet.destroy
- そのレコードを DBから削除 します(1行削除)。
- 関連を設定している場合は、オプションによって子レコードも連動削除されることがあります。
-
-
private
- 以降に定義したメソッドは 外部から呼び出せない(コントローラのアクションにはならない)範囲になります。
- これにより、
tweet_params
のような 補助メソッドをアクションとして誤呼び出しされない ように守れます。 -
params.require(:l07_tweet).permit(...)
- 受け取ったパラメータから、まず
require
で必須キー(外側の名前) を指定します。 - その中で
permit
に書いたキーだけ を 安全に受け付け ます(それ以外は捨てられる)。
- 受け取ったパラメータから、まず
@tweet = Tweet.〇〇
と tweet = Tweet.〇〇
の違い
-
@tweet
(インスタンス変数)- ビュー(.html.erb)から見える変数。
- コントローラ内でセットすると、同じリクエストのビューなどから参照できる。
- 例:
@tweets = Tweet.all
→index.html.erb
で@tweets.each
が使える。
-
tweet
(ローカル変数)- そのメソッドの中だけで使える一時的な変数。
- ビューからは見えない。
tweet
に入れてもテンプレート側では参照できない。
役割と使い分け(超実務的ルール)
-
画面に出したいデータ(一覧・詳細・フォーム初期値)は
@...
に入れる。- 例:
index/new/show/edit
→@tweets
,@tweet
- 例:
-
画面に出さない“処理用の値”(保存・更新・削除だけ)なら ローカル変数で十分。
- 例:
create/update/destroy
→tweet = Tweet.new(...)
など
- 例:
4-4. index.html.erb
app/views/l07/tweets/index.html.erb
<h1>一覧</h1>
<%= link_to "新規作成", new_l07_tweet_path %>
<div class="tweets-container">
<% @tweets.each do |t| %>
<div class="tweet">
<%= t.title %>
<%= t.body %>
<%= link_to "詳細", l07_tweet_path(t) %>
</div>
<% end %>
</div>
コードの解説
-
@tweets.each
- 一覧用コレクションをループ表示。
-
l07_tweet_path(t)
- 詳細ページのパスヘルパ(
GET /l07/tweets/:id
)。
- 詳細ページのパスヘルパ(
4-5. show.html.erb
app/views/l07/tweets/show.html.erb
<h1>詳細</h1>
<div class="tweet">
<p><%= @tweet.title %></p>
<p><%= @tweet.body %></p>
<p><%= @tweet.created_at %></p>
<p><%= link_to "編集", edit_l07_tweet_path(@tweet) %></p>
<p><%= button_to "削除", l07_tweet_path(@tweet), method: :delete %></p>
</div>
<%= link_to "一覧に戻る", l07_tweets_path %>
コードの解説
-
@tweet.title
/@tweet.body
- Controller の
@tweet
を表示。
- Controller の
-
edit_l07_tweet_path(@tweet)
- 編集フォーム(
GET /l07/tweets/:id/edit
)。
- 編集フォーム(
-
button_to ... method: :delete
- 削除(
DELETE /l07/tweets/:id
)。
- 削除(
4-6. new.html.erb
app/views/l07/tweets/new.html.erb
<h1>新規作成</h1>
<%= form_for @tweet do |f| %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title, :size => 30 %>
</div>
<div class="field">
<%= f.label :body %>
<%= f.text_area :body, :size => "30x2" %>
</div>
<%= f.submit "投稿する" %>
<% end %>
<%= link_to "一覧に戻る", l07_tweets_path %>
コードの解説
-
form_for @tweet
-
新規(未保存) なので送信先は
POST /l07/tweets
(create
)。
-
新規(未保存) なので送信先は
4-7. edit.html.erb
app/views/l07/tweets/edit.html.erb
<h1>編集</h1>
<%= form_for @tweet do |f| %>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title, :size => 30 %>
</div>
<div class="field">
<%= f.label :body %>
<%= f.text_area :body, :size => "30x2" %>
</div>
<%= f.submit "更新する" %>
<% end %>
<%= link_to "一覧に戻る", l07_tweets_path %>
コードの解説
-
form_for @tweet
-
保存済みのため送信先は
PATCH /l07/tweets/:id
(update
)。
-
保存済みのため送信先は
5. 動作確認
コマンドプロンプト
rails db:migrate
rails s
1. 一覧を開く: /l07/tweets
→ エラー①(undefined method 'each' for nil:NilClass
)を確認
2. エラー①修正: 修正し再読み込み
3. 新規作成: タイトル+本文で投稿 → 一覧に戻る
4. 現象確認: タイトルが未表示
5. エラー②修正: 修正し再読み込み
6. 再作成: 新規作成&編集で title が表示・更新されることを確認
6. エラーの解答
(A) NoMethodError
app/controllers/l07/tweets_controller.rb
def index
- tweet = L07::Tweet.all # ローカル変数
+ @tweet = L07::Tweet.all # インスタンス変数にする
end
-
原因: View は
@tweet
を参照するのに、Controller がtweet
に代入しているため@tweet
がnil
。 -
修正方法:
tweet
を@tweet
に直す。
(B) title が表示されない
app/controllers/l07/tweets_controller.rb
private
def tweet_params
- params.require(:tweet).permit(:body) # NG: title を許可していない
+ params.require(:tweet).permit(:title, :body) # OK: title を追加
end
-
原因:
permit
に無いキーは捨てられるため、title が保存されない。 -
修正方法:
permit
に:title
を追加する。
7. 確認テスト(満点合格)