💬

【Rails】バリデーション

2023/06/24に公開

バリデーションとは

データの妥当性をチェックするための機能です。データベースに保存する前に、モデルの属性に対してルールや条件を設定することで、正しいデータのみを受け入れるようにします。

バリデーションの設定

バリデーションを設定するには、モデルのファイルに設定内容を記述します。
ここでは、本の投稿サイトを例に解説します。

Bookモデルに記述追加

app/models/book.rb
class Book < ApplicationRecord
  belongs_to :user
  
+ validates :title, presence: true
+ validates :body, presence: true, length:{maximum:200}
end
  1. validates :title, presence: true :
    title 属性に対して「必須入力」であるというバリデーションを設定しています。つまり、title が未記入の場合にバリデーションエラー―が発生します。。

  2. validates :body, presence: true, length:{maximum:200}:
    body 属性に対して「必須入力かつ最大200文字」というバリデーションを設定しています。つまり、bodyが未記入または200文字を超える場合にバリデーションエラーが発生します。

コントローラに記述追加

app/controllers/books_controller.rb
class BooksController < ApplicationController

  def create
+  @book = Book.new(book_params)
+  @book.user_id = current_user.id
+  if @book.save
+     redirect_to book_path(@book.id)
+  else
+    @books = Book.all
+    render :index
+  end
  end

  def update
+  @book = Book.find(params[:id])
+  if @book.update(book_params)
+    redirect_to book_path(@book.id)
+  else
+    render :edit
+  end
  end

:

  private

:

end

コントローラの記述について解説します。

  1. if @book.save:
    if式を用いて、対象のカラムにデータが入力されればsaveメソッドでtrueが返されます。

  2. redirect_to book_path(@book.id):
    @bookが正常に保存された場合、book_pathへのリダイレクトが行われます。

  3. @books = Book.all:
    @booksというインスタンス変数に、データベース内のすべてのBookレコードを代入しています。この行の目的は、エラーが発生した場合にエラーメッセージと共にBookの一覧を再表示することです。

  4. render :index:
    renderは、同じアクション内で別のビューを表示するために使用します。ここではindexビューを再表示します。

render vs redirect_to

以下の表に、renderredirect_toの違いをまとめます。

render redirect_to
動作 同じアクション内でビューを表示 別のアクションやURLにリダイレクト
処理の継続 現在のアクション内で処理を続行 別のアクションやURLで新しいリクエストとレスポンスを生成
レスポンス 同じリクエスト内でビューが表示される ブラウザが新しいURLにリダイレクトし、新しいページが表示される
用途 同じアクション内で処理を反映させる 別のアクションやURLで処理を行いたい場合
シナリオ バリデーションエラーの表示など データの作成・更新後に詳細ページにリダイレクト

上記の表を見ると、以下のポイントがわかります:

  • renderは、同じアクション内で処理を続行し、ビューを表示します。データの処理やバリデーションエラーなど、同じアクション内で解決可能な場合に使用されます。
  • redirect_toは、別のアクションやURLに制御を移し、新しいリクエストとレスポンスを生成します。データの作成や更新後に詳細ページにリダイレクトする場合など、別のアクションやURLで処理を行いたい場合に使用されます。

このように、renderredirect_toはそれぞれ異なる目的と動作を持ち、適切な場面で使い分ける必要があります。

Viewの修正

バリデーションが行われた際、エラーになった時に画面上でエラーの内容が表示されるようにします。
バリデーションエラーメッセージを複数のViewページで表示したいので、共通レイアウトとし部分テンプレートにします。
部分テンプレートについてはまた別の記事で解説しますね。

エラーメッセージView(部分テンプレート)

app/views/layouts/_errors.html.erb
<% if obj.errors.any? %>
  <div id="error_explanation">
    <h3><%= pluralize(obj.errors.count, "error") %> prohibited this obj from being saved:</h3>
    <ul>
      <% obj.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

エラーメッセージの共通レイアウトについて解説します。

  1. <% if obj.errors.any? %>
    errorsメソッドはモデルに対して発生したエラーを格納しているオブジェクトです。any?メソッドを使うことで、エラーの有無を判定しています。もしエラーが発生していれば、条件内のコードが実行されます。objは単なる仮の変数名であり、実際に表示したいオブジェクトに応じて適切な変数名に置き換える必要があります。部分テンプレートファイルを呼び出す記述で置き換えます。

  2. <div id="error_explanation">
    エラーメッセージを表示するためのdiv要素を定義しています。id属性を指定しているため、CSSやJavaScriptでスタイリングや操作を行うことができます。

  3. <%= pluralize(obj.errors.count, "error") %> prohibited this obj from being saved:
    エラーメッセージの見出し部分です。pluralizeメソッドを使用して、エラーメッセージの数に応じて単数形と複数形を使い分けています。この部分では、エラーメッセージの数と"error"という単語を組み合わせて表示しています。(ex.エラー1個:error, エラー2個:errors)
    prohibited以降は表示される文章です。

  4. <ul>
    エラーメッセージをリスト形式で表示するためのul要素を使用します。

  5. <% obj.errors.full_messages.each do |message| %>
    エラーメッセージの配列であるfull_messageseachを使いループ処理しています。各エラーメッセージはmessageという変数に代入されます。

  6. <li><%= message %></li>
    各エラーメッセージをli要素で囲み、順番に表示します。

部分テンプレートファイルの呼び出し

一覧画面と編集画面で上記の共通レイアウトを呼び出します。

  • 一覧画面
app/views/books/index.html.erb
+ <%= render "layouts/errors", obj: @book %>

 <div class="container">
   <div class="row">
     <div class="col-md-3">
       <h2>User info</h2>
       <h2>New book</h2>
       <%= render "form", book: @book %>
     </div>
     <div class="col-md-8">
       <h2>Books</h2>
       <%= render "index", books: @books %>
     </div>
   </div>
 </div>
  • 編集画面
app/views/books/edit.html.erb
 <div class="container">
  <div class="row">
    <div class="mx-auto">
     <h1>Editing Book</h1>
+     <%= render "layouts/errors", obj: @book %>
      <%= render "form", book: @book %>
      <%= link_to "Show", @book %> |
      <%= link_to "Back", books_path, class: "back" %>
    </div>
  </div>
 </div>

以下のように記述することで、部分テンプレートファイルを呼び出せるようにしました。

<%= render "layouts/errors", obj: @book %>

上記のコードではrenderメソッドを使用して部分テンプレートファイルである"layouts/errors"を呼び出しています。さらに、オプションとしてobj : @bookを指定しています。
このobj: @bookというオプションは、部分テンプレートに渡すデータを指定するためのものです。ここでは@bookというインスタン変数を部分テンプレートに渡すことができます。
部分テンプレート内では、渡された@bookオブジェクトを利用して、エラーメッセージの表示や他の処理を行うことができます。なお、@bookが使われていますが、実際には他のオブジェクトも渡すことができます。

表示されるエラー文の確認


最初この課題に取り組んだ時は、エラーメッセージを部分テンプレートにすることができなかったなー。複数の場面で使うので、部分テンプレートにしておいた方が使いやすい。
模範解答のコードを見て学ばせてもらいました。
上手なコードを読むことで、よりよいアプローチが発見できました☆

Discussion