【Ruby on Rails】CRUD操作を行うアプリケーションの作成手順
前回、Ruby on Railsを全くゼロの状態からプロジェクトを作成し、DBから取得したデータをブラウザに表示したので、
その続きでアプリケーションにCRUD(作成、読み取り、更新、削除)を実装する方法について。
目次
- ルーティングの追加(show)
- アクションの追加(show)
- ビューの編集(show)
- ブラウザに表示
- リソースフルルーティング
- pathヘルパーを使ったリンク設定
- link_toヘルパーへの置き換え
- newとcreateアクションの作成
- フォームの作成
- ストロングパラメータの設定
- articlesトップにフォームへのリンクを追加
- editとupdateアクションの作成
- パーシャル(共有ビュー)の作成
- 編集ページへのリンク追加
- 削除処理の作成
- ボタンリンクのレイアウト調整
## 1. ルーティングの追加(show) まずは、ルーティングを追加する。 config/routes.rbに以下を記述。
Rails.application.routes.draw do
root "articles#index"
get "/articles", to: "articles#index"
get "/articles/:id", to: "articles#show"
end
・get "/articles/:id", to: "articles#show"
/articles/整数 のURIのアクセスしたら、articlesコントローラのshowアクションを実行する。
・:id
URIに:パラメータ名
とすることでデータを渡すことができる。
▼パラメータの受け取り
params([:パラメータ名]) でデータを受け取ることができる。
ルーティング: /articles/:id
↓
URL: articles/5
↓
コントローラ: params([:id]) #イコール5になる。
params
があったら、getメソッドでデータが渡されているんだな。と理解しておけばOK。
## 2. アクションの追加(show) "articles#show"の処理を作成する。 `app/controllers/articles_controller.rb`に以下を追記。
def show
@article = Article.find(params[:id])
end
テーブルarticlesの中の、URIの末尾で渡された整数番目のデータを取得し、@article
(単数形)に代入する。
たくさんあるテーブルのデータのうち1行のみを指定して抜き出しているため、変数名は単数形にしている。
・@ の意味
コントローラの中の@変数
をインスタンス変数とよび、インスタンス毎に異なる変数を定義できる。
@@変数
の場合はクラス変数とよび、クラスで固有(どのインスタンスでも同じ)な変数となる。
(参考)【Ruby】クラスの中のアットマークの意味。@と@@の違いは?
## 3. ビューの編集(show) "articles#show"実行後に表示するビューを作成する。 views > articles > show.html.erb を作成する。
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
コントローラのshowアクションで、articlesテーブルから取得したデータ@article
の中のtitle
とbody
を表示している。
4. ブラウザに表示
http://localhost:3000/articles/1
にアクセスして、ページが正しく表示されるか確認する。
サーバーが停止している場合は以下で起動。
$ bin/rails server
## 5. リソースフルルーティング CRUD操作のR(読み取り)のためにshowアクションを設定した。 Ruby on Railsには便利なCRUDのための超便利なリソースフルルーティングという機能がある。
routes.rbに以下を記述すると、CRUDのルーティングを自動生成してくれる。
・resources :ルート名
なお、ルーティングの確認はbin/rails routes
コマンドでできる。
**▼ルーティングの確認**
Rails.application.routes.draw do
root "articles#index"
get "/articles", to: "articles#index"
get "/articles/:id", to: "articles#show"
end
$ bin/rails routes
Prefix Verb URI Pattern Controller#Action
root GET / articles#index
articles GET /articles(.:format) articles#index
GET /articles/:id(.:format) articles#show
↓ リソースフルルーティングに変更
Rails.application.routes.draw do
root "articles#index"
resources :articles
end
$ bin/rails routes
Prefix Verb URI Pattern Controller#Action
root GET / articles#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
HTTP動詞にPOSTやDELETE、ルートにnew, :id/editが追加されている。
右の方にスクロールしていくと、index, create, new, edit, show, update, destroyアクションと対応していることがわかる。
## 6. pathヘルパーを使ったリンク設定 リソースフルルーティングを使うと、pathヘルパーも使えるようになる。
・ルート名_path(対応するテーブルの行データ)
この記述で、対象のページへのURIを表示することができる。
articlesトップページに各ページへのリンクを追加
resources :articles
でCRUDのルーティングを作成した場合、articles_path
ヘルパーが作成される。引数でArticleモデル(articlesテーブル)の1行のデータを渡すと、そのページへのリンクを自動生成してくれる。
これを利用して、index.html.erbページに各個別ページへのリンクを設置する。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<a href="<%= article_path(article) %>">
<%= article.title %>
</a>
</li>
<% end %>
</ul>
articles#indexコントローラから受け取った@articles
のデータを一つづつ抜き出し、article_path(article)
で各個別ページへのリンクを生成している。
**▼ブラウザでの表示** http://localhost:3000/articles/
![]() |
---|
article.title名でリンクが表示され、クリックすると個別ページに移動することができる。
## 7. link_toヘルパーへの置き換え aタグとpathヘルパー、アンカーテキストの組み合わせは、`link_to`を使うと簡単に記述できる。
・link_to "アンカーテキスト", ルート名
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to article.title, article %>
</li>
<% end %>
</ul>
**▼ブラウザでの表示** http://localhost:3000/articles/
![]() |
---|
pathヘルパーを使った場合と同じ内容が表示される。
## 8. newとcreateアクションの作成 CRUD操作のC(作成)に当たるアクションを作成する。作成するアクションは2つ。(1)データ入力のためのform用にnewアクション。(2)データをDBに保存するためのcreateアクション。
▼newアクション
- モデルから新しいオブジェクトを作成。
- formを表示するために使用する(saveではない)
- 対象のビューは、new.html.erb
▼createアクション
- 作成した新しいオブジェクトにデータを追加
- saveでテーブルに保存する目的。
- save処理がうまくいけば、その記事ページにリダイレクト。
/articles/#{@article.id}
- save処理がうまくいかない場合はnewアクションに飛ばす
app/controllers/articles_controller.rb
def new
@article = Article.new
end
def create
@article = Article.new(title: "...", body: "...")
if @article.save
redirect_to @article
else
render :new
end
・if @article.save
変数.save
はSQLで INSERT INTO~を実行した後、最後にbooleanを返すため、if文の条件式として使うことができる。
・render :アクション名
renderでアクション名を指定すると、そのアクションを実行することができる。
redirect_toとrenderの違い
saveが成功したときのページ繊維はredirect_to
、失敗したときはrender
ヘルパーを使うのには意味がある。
redirect_to
は新しいリクエストを作成してページ遷移する。対して、render
は現在のリクエストのまま指定のページを開く。
このため、DBのデータに変更があった場合は、新しいリクエストで通信し直す必要があるため、redirect_to
を使う。
## 9. フォームの作成 newアクションで開くフォームを作成する。 app/views/articles/new.html.erb
<h1>New Article</h1>
<%= form_with model: @article do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
rubyでformを簡単に作るためにヘルパーが用意されている。(form builderと呼ぶ)。
form builderで使うメソッドは、label, text_field(またはtext_area), submitの3つだけ。
・form_with
form builderインスタンスを生成する。
<%= form_with model: モデルのインスタンス do |form| %>
・form.label
入力フォームにラベル(テキスト)をつける。指定は文字列でも、プロパティ名でも可。
<%= form.label :プロパティ名 %>
<%= form.label "テキスト" %>
<%= form.label "テスト#{:プロパティ名}テスト" %>
**▼コンパイル例**
<%= form.label :title %>
↓ htmlにコンパイル
<label for="article_title">Title</label>
#### ・`form.text_field` 入力ボックス(input type="text")を作成する。
<%= form.text_field :プロパティ名 %>
コンパイルすると対応するidとname属性が生成される。
**▼コンパイル例**
<%= form.text_field :title %>
↓ htmlにコンパイル
<input type="text" name="article[title]" id="article_title">
#### ・`form.submit` 送信ボタンを作成する。
<%= form.submit %>
↓ htmlにコンパイル
<input type="submit" name="commit" value="Save " data-disable-with="Save ">
デフォルトではSaveの文字が表示される。
▼文字列の追加とクラスの追加
文字列は " "
で記述。
クラス名は,class:'クラス名'
※カンマ必須。(他のヘルパーも同様)
<%= form.submit "送信", class:"submit-button" %>
↓ htmlにコンパイル
<input type="submit" name="commit" value="送信" class="submit-button" data-disable-with="送信">
## 10. ストロングパラメータの設定 悪意のあるユーザーによるDBのデータ追加を防ぐため、コントローラにプライベートメソッドを追加する。
app/controllers/articles_controller.rb
10-1. プライベートメソッドの追加
private
def article_params
params.require(:article).permit(:title, :body)
end
articleオブジェクトで変更・追加できるカラムは、title
とbody
のみという制限を付与。
#### (補足)エラー undefined method `errors' 次のようなエラーが発生した場合は、コントローラにプライベートメソッドが追加されていない可能性がある。
undefined method `errors' for nil:NilClass
### 10-2. バリデーションの追加 モデルにバリデーションを追加する。
・validates :カラム名, バリデーションルール
app/models/article.rb
class Article < ApplicationRecord
validates :title, presence: true
validates :body, presence: true, length: { minimum: 10 }
end
### 10-3. エラーメッセージの表示設定 バリデーションでfalseになったときのエラーメッセージはビューの中に記述する。
<% @モデルのインスタンス名.errors.full_messages_for(:プロパティ).each do |message| %>
<div><%= message %></div>
<% end %>
app/views/articles/new.html.erb
<h1>New Article</h1>
<%= form_with model: @article do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
<% @article.errors.full_messages_for(:title).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %><br>
<% @article.errors.full_messages_for(:body).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
この状態では、エラーメッセージが設定されていないため表示されない。
## 11. articlesトップにフォームへのリンクを追加 `link_to`ヘルパーを使う。パスはpathヘルパーを使う
<%= link_to "アンカーテキスト", アクション名_インスタンス名_path %>
app/views/articles/index.html.erb
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to article.title, article %>
</li>
<% end %>
</ul>
<%= link_to "New Article", new_article_path %>
## 12. editとupdateアクションの作成 CRUDの「U」(更新)を行うためのアクションを作成する。必要なアクションは2つ。
(1)editアクションで、DBから指定したidのデータを取得して表示する。
(2)updateアクションで、変更内容をDBに反映する。
app/controllers/articles_controller.rb に以下を追記。
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render :edit
end
end
・`モデルのインスタンス.update(更新データ)` 更新データとして、article_params関数が渡されている。これは、先ほどストロングパラメータとして定義したもの。
データ更新時にバリデーションを実行して、その結果がupdateの引数(更新データ)になる。
updateの引数にストロングパラメータを指定するのが一般的。
private
def article_params
params.require(:article).permit(:title, :body)
end
class Article < ApplicationRecord
validates :title, presence: true
validates :body, presence: true, length: { minimum: 10 }
end
## 13. パーシャル(共有ビュー)の作成 newとeditで使うビューは全く同じものになる。このため、共有用のビュー(パーシャルや部分テンプレートと呼ぶ)を作成して、newとeditそれぞれのビューでパーシャルを読み込むようにする。
13-1. パーシャルの作成
パーシャルのファイル名は冒頭にアンダースコアをつけ、共有用であることがわかるようにする。
呼び出すビューと同じディレクトリに作成する。
app/views/articles/_form.html.erb
<%= form_with model: article do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
<% article.errors.full_messages_for(:title).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %><br>
<% article.errors.full_messages_for(:body).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
▼注意点
パーシャルを使わず直接表示した場合、受け取るデータを@をつけて@モデルのインスタンス名
で記述したが、パーシャルは@ が不要になる。
これは、パーシャルを呼び出す時に、@ のつかないプロパティ名で渡す記述をするため(次で作成)
### 13-2. パーシャルの呼び出し renderヘルパーを使って、パーシャルを呼び出す。
・<%= render "form", 渡すプロパティ名: @モデルのインスタンス名 %>
以下2ファイルでパーシャルを呼び出す記述を追記。
app/views/articles/new.html.erb
app/views/articles/edit.html.erb
<h1>New Article</h1>
<%= render "form", article: @article %>
<h1>Edit Article</h1>
<%= render "form", article: @article %>
どちらのビューも呼び出し方は同じ。h1が異なるのみ。
### 13-3. ブラウザで確認 http://localhost:3000/articles/1/edit にアクセスする。
DBのデータが表示されていればOK。
## 14. 編集ページへのリンク追加 `link_to`を使って、記事詳細ページ(show)と一覧ページ(index)に編集ページ(edit)へのリンクを追加する。
ページの指定はpathヘルパーを使う。引数でデータを渡す。
app/views/articles/show.html.erb
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<ul>
<li><%= link_to "Edit", edit_article_path(@article) %></li>
</ul>
app/views/articles/index.html.erb
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to article.title, article %> | <%= link_to "Edit", edit_article_path(article) %>
</li>
<% end %>
</ul>
<%= link_to "New Article", new_article_path %>
## 15. 削除処理の作成 ### 15-1. コントローラにdestroyアクションを追加 CRUDの「D」(削除)処理をコントローラに追加する。
app/controllers/articles_controller.rb
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to root_path
end
### 15-2. 削除リンクの追加 `button_to`ヘルパーを使って、詳細ページ(show)と一覧ページ(index)に削除リンクを追加する。
削除処理のURLはarticle
でshowやupdateアクションと同じ。HTTP動詞でdeleteを選択することで振り分けを行う。
<li><%= button_to "Destroy", article_path(@article), method: :delete} %>
・method: :メソッド名
アクセスするメソッドを指定する。デフォルトはGET。
▼注意点
公式のチュートリアルではlink_to
が使われているが、GETメソッドで飛んでしまい削除できないため、button_to
を使う。
confirmはbutton_toでは機能しない(rails v6)
・data: {confirm: 文字列}
リンク先に飛ぶ前に確認ダイアログを表示する。
app/views/articles/show.html.erb
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<ul>
<li><%= link_to "Edit", edit_article_path(@article) %></li>
<li><%= button_to "Delete", article_path(@article), method: :delete %></li>
</ul>
![]() |
---|
ボタンとリンクが組み合わさって不自然。。
app/views/articles/index.html.erb
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to article.title, article %> |
<%= link_to "Edit", edit_article_path(article) %> |
<%= button_to "Delete", article_path(article), method: :delete %>
</li>
<% end %>
</ul>
<%= link_to "New Article", new_article_path %>
## 16. ボタンリンクのレイアウト調整 `button_to`で作成したボタンを、アンカーテキストと同じになるようCSSで調整する。
![]() |
---|
コントローラを作成した時に同時に生成されるscssファイルを編集する。
app/assets/stylesheets/articles.scss
button_toをコンパイルしたタグにはclass="button_to"
がついているのでそれを利用。
.button_to > input{
//ボタンのcssをキャンセル
border: none;
outline: none;
background: transparent;
//リンクのcssに合わせる
font-size: 16px;
text-decoration: underline;
color: #640764;
padding: 0;
font-family: initial;
}
![]() |
---|
だいたい似た感じになった。(ざっくりなので、カラーコードやhoverの設定など適宜調べてください)
今回はここまで。次回はCommentモデルの生成と、モデルの関連づけ。