はじめに
Ruby on Rails初心者の学習記録Part3です。
Part1では、Rubyの基本文法やRailsを利用したWebアプリケーションの開発環境構築手順及びルーティングについて学びました。
Part2では、MVCアーキテクチャのモデルについて学びました。
Part3とPart4では、 "Getting Started with Rails" の "7 CRUDit Where CRUDit Is Due" を教材にして、CRUDを学んでいきたいと思います。まずPart3では、CRUDの Create と Read を学びます。Railsには、CRUDをシンプルにコーディングできる多くの機能があるようなので、実際にコーディングしながらそれらの機能を扱っていければと思っています。
1. 記事の表示
最初はCRUDの R(Read) を扱います。
Part2で、記事一覧画面を作成しました。次は各記事を表示する画面を作成します。
ⅰ. ルーティングの追加
まず、新しいルーティングを追加します。config/routes.rb
を開き、get "/articles/:id", to: "articles#show"
を追加します。
Rails.application.routes.draw do
root "articles#index"
get "/articles", to: "articles#index"
get "/articles/:id", to: "articles#show"
end
ⅱ. コントローラーへのアクションの追加
ⅰ. で追加したパスの末尾の:id
は、ルーティングのパラメーターを指定します。ルーティングパラメーターはリクエストパスの特定の値を取得し、params
というハッシュ内にその値を追加します。params
はコントローラーのアクションからアクセスできます(コントローラーのshow
というアクション内でparams[:id]
のようにしてアクセスできます)。
では、app/controllers/articles_controller.rb
内にshow
アクションを追加します。
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@articles = Article.find(params[:id])
end
end
show
アクションはルーティングパラメーターで取得したIDを引数にArtcle.find
をコールしています(findについては、Part2でも扱いました)。戻り値は@article
インスタンス変数内に保存され、ビューからアクセスできるようになります。
ⅲ. ビューの追加
デフォルトでは、ⅱ. で追加したshow
アクションはapp/views/articles/show.html.erb
にレンダリングされます。
そのため、app/views/articles/show.html.erb
を作成します。
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
これで http://localhost:3000/articles/1 にアクセスすると、1つ目の記事が表示されます。
最後に記事一覧画面から記事画面へ遷移するリンクを追加します。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<a href="/articles/<%= article.id %>">
Title: <%= article.title %>
</a>
</li>
<% end %>
</ul>
<p>Find me in app/views/articles/index.html.erb</p>
2. Resourceful Routing
Create を扱う前により便利なルーティング方法があるようなので、確認します。
1.で実施したように新しくCRUDを追加する際は、以下3つ作業が必要になります。
- ルーティングの追加
- コントローラーへのアクションの追加
- ビューの追加
ルーティング、コントローラーのアクション、ビューはいつでもエンティティのCRUD操作として機能します。このようなエンティティは resource (リソース) と呼ばれます。例えば、今開発しているアプリケーションでは、1つの記事が1つのリソースに該当します。
Railsはresources
というルーティングメソッドを提供しています。resources
はarticles
といったリソースのコレクション向けの全てのルーティングを紐づけます。
ということで、config/routes.rb
内のget
を利用したルーティングをresources
に置き換えます。
Rails.application.routes.draw do
root "articles#index"
# Before
# get "/articles", to: "articles#index"
# get "/articles/:id", to: "articles#show"
# After
resources :articles
end
以下のコマンドでルーティングの紐づけを確認できます。
rails routes
resources
メソッドはURLとパスのヘルパーメソッドも設定します。パスのヘルパーメソッドにより、コードが特定のルーティング設定に依存することを防げます。Prefixカラムの値には、_url
や_path
といったサフィックスが追加されます。例えば、article_path
ヘルパーは、記事を1件渡されると、"/articles/#{article.id}"
を返します。これによりapp/views/articles/index.html.erb
内のリンクを簡潔にできます。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
# Before
# <a href="/articles/<%= article.id %>">
# After
<a href="<%= article_path(article) %>">
Title: <%= article.title %>
</a>
</li>
<% end %>
</ul>
<p>Find me in app/views/articles/index.html.erb</p>
さらにlink_to
ヘルパーを使うことでより便利にできます。link_to
ヘルパーは第一引数をリンクのテキスト、第二引数をリンク先として、リンクをレンダリングします。モデルオブジェクトを第二引数にすると、link_to
は適切なパスヘルパーを呼び出してオブジェクトをパスに変換します。例えば、articleを渡すとlink_to
はarticle_path
をコールします。これを利用すると、 app/views/articles/index.html.erb
を以下のように変えることができます。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<a href="<%= article_path(article) %>">
# Before
# Title: <%= article.title %>
# After
Title: <%= link_to article.title, article %>
</a>
</li>
<% end %>
</ul>
<p>Find me in app/views/articles/index.html.erb</p>
より詳しいルーティングの説明は、"Rails Routing from the Outside In" に記載があります。
3. 記事の作成
リソースフルルーティングを学んだので、CRUDの学習に戻ります。ここでは、Createを扱います。Webアプリケーションでは、新しいリソースを作成する際、以下のように複数のプロセスが必要になります。
Rialsを利用したアプリケーションでは、コントローラーnew
とcreate
アクションによって通常これらのステップは制御されます。では、これらのアクションをapp/controllers/articles_controller.rb
のshow
アクションの下に追加します。
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(title: "...", body: "...")
if @article.save
redirect_to @article
else
render :new, status: :unprocessable_entity
end
end
end
new
アクションは新しい記事をインスタンス化しますが、保存はしません。この記事はフォーム作成時にビュー内で利用されます、デフォルトでは、new
アクションは次に作成するapp/views/articles/new.html.erb
でレンダリングされます。
create
アクションは、タイトルと本文を持つ新しい記事をインスタンス化し、保存します。保存が成功すれば、その記事の画面("http://localhost:3000/articles/#{@article.id}"
)にリダイレクトします、失敗したら、app/views/articles/new.html.erb
で 422 Unprocessable Entity というステータスコードを表示します。ここのタイトルと本文はダミーの値が入っています。フォームが作成された後、ユーザーが変更します。
redirect_to
を使うとブラウザで新しいリクエストが発生しますが、render
は指定のビューを現在のリクエストとしてレンダリングします。そのため、データベースやアプリケーションのステートが変更された後にredirect_to
を利用することが重要です。そうしないとユーザーが画面をリロードしたら、同じリクエストが再送信され、変更が重複してしまいます。
ⅰ. Form Builderの利用
フォームを作成するために Form Builder というRailsの機能を利用します。Form Builderを使うことで、最小限のコードでRailsの規約に沿った設定が全てできあがったフォームが作成できます。
それでは、app/views/articles/new.html.erb
を作成します。
<h1>New Article</h1>
<%= form_with model: @article do |form| %>
<div>
<%= form.label :title %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :bidy %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
form_with
ヘルパーメソッドがForm Builderをインスタンス化します。フォーム要素を出力するためにform_with
ブロック内でlabel
やtext_field
のようなメソッドをコールします。
より詳しいForm Builderの説明は、"Action View Form Helpers" に記載があります。
これで http://localhost:3000/articles/new にアクセスすると、新規記事作成画面が表示され、記事のタイトルと本文を入力することができます。
ⅱ. Strong Parametersの利用
送信されたフォームデータはparams
ハッシュ内に保存され、ルーティングパラメータも同様に取得されます。そのため、create
アクションでは、タイトルはparams[:article][:title]
から取得でき、本文はparams[:article][:body]
から取得できます、これらの値をそれぞれArticle.new
へ渡すこともできますが、複雑化するとエラーの発生確率も増えます。
その代わりに1つのハッシュに全ての値を含めて渡します。しかし、その場合もハッシュ内でどの値が許容されているか指定しなければなりません。そうしないと悪意のあるユーザーがフィールを追加し、プライベートなデータを上書きすることができます。実際には、フィルターがないparams[:article]
ハッシュを直接Article.new
に渡すと、RailsはForbiddenAttributesError
を出力します。そのため、params
をフィルターするために strong parameters というRailsの機能を利用します。
それでは、app/controllers/articles_controller.rb
の最後にparams
をフィルターするarticle_params
を定義します。そして、article_params
をcreate
アクションで使うよう変更します。
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render :new, status: :unprocessable_entity
end
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
より詳しいStrong Parametersの説明は、"Action Controller Overview § Strong Parameters" に記載があります。
ⅲ. バリデーションとエラーメッセージ
これまで見てきたようにリソースの作成は複数のプロセスがありました。ここでは、その中の1つであるバリデーションを扱います。Railsにはバリデーション機能があります。バリデーションは、モデルオブジェクトが保存される前に実施されます。エラーがあった場合、保存はされず、モデルオブジェクトのerrors
属性にエラーメッセージが追加されます。
それでは、app/models/article.rb
内のモデルにいくつかバリデーションを追加します。
class Article < ApplicationRecord
validates :title, presence: true
validates:body, presence: true, length: { minimum: 10 }
end
Active Recordは全てのテーブルのカラムにモデル属性を自動的に定義します。そのため、モデルファイル内でtitle
やbody
属性を定義する必要はありません。
バリデーションを追加したので、エラーメッセージを表示させるために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_message_for(:title).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :bidy %>
<% @article.errors.full_message_for(:body).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
full_messages_for
メソッドは、エラーメッセージの配列を返します。エラーがない場合、配列は空になります。
これらがどのように動作しているか理解するために改めてコントローラーのnew
とcreate
のアクションを確認します。
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render :new, status: :unprocessable_entity
end
end
-
http://localhost:3000/articles/new アクセス時
-
new
アクションにマッピングされるため、保存されない。そのため、バリデーションは発生しない。
-
- フォーム送信時
-
create
アクションにマッピングされるため、保存される。そのため、バリデーションが発生する。
-
より詳しいバリデーションの説明は、"Active Record Validations" に記載があります。また、より詳しいバリデーションエラーメッセージの説明は、"Active Record Validations § Working with Validation Errors" に記載があります。
ⅳ. 仕上げ
仕上げとして、記事一覧画面(app/views/articles/index.html.erb
)に記事作成画面のリンクを追加します。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<a href="<%= article_path(article) %>">
Title: <%= link_to article.title, article %>
</a>
</li>
<% end %>
</ul>
<%= link_to "New Article", new_article_path %>
これで記事一覧画面から新規記事作成画面へ遷移することができるようになります。
最後に
今回は"Getting Started with Rails" の "7 CRUDit Where CRUDit Is Due" を教材にして、CRUD の Create と Read を学びました。Railsでは、Form Builder や Strong Parameters などCRUD実装に役立つ機能が多数提供されていました。
次も引き続きCRUDを学んでいきます。次は残りの Update と Delete を学びます。
最後までお読みいただきありがとうございました!