0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rails7 で TODOアプリを作ろう ④ (statusをenumへ)

Last updated at Posted at 2023-11-03

はじめに

前回は

TaskのViewにBootstrap5を適用として

  • bootstrap5

について学びました。

今回は

statusをEnumへ として

  • バリデーション
  • enum

について学びます。

では、はじめていきましょう。

1. バリデーション

最初に仕込みとなりますが、最低限のバリデーション設定をしておきます。

バリデーション

バリデーションとはデーターの入力に対して制限を掛ける事で、空入力や多すぎる文字を防いだり、不正な入力を弾く事ができる機能です。

データーの登録前にチェックしようという事ですね。

SQLのエラー

現在、データーを空投稿しようとすると、

Image from Gyazo

このようなエラーが出ます。

これは、マイグレーションファイルを作った時にDBに「必須入力」の制約を入れている為です。

このエラーを防ぐために、モデルにバリデーションを書き入れておきます。

Model

バリデーションの記述は Model に書いていきます。

現状 app/models/task.rb

app/models/task.rb【確認】

class Task < ApplicationRecord
end

現状 class 以外は何も記述がありません。

実は、モデルのCRUD操作やクエリ操作を行う為の小難しい記述は、継承元の ApplicationRecord や、さらにその継承元に書かれていますので、モデル単体には該当のテーブルの操作に関するロジックのみを書いていく事ができます。

では、実際に書いてみましょう。

app/models/task.rb【追記】

class Task < ApplicationRecord
  validates :title, presence: true, length: { minimum: 3, maximum: 20 }
  validates :description, allow_blank: true, length: { minimum: 3, maximum: 500 }
  validates :start_time, presence: true
end

このように validates で宣言を行って、各カラムに対して制約を書いていきます。

presence: true は必須入力となり、length 入力出来る文字数を制限しています。

allow_blank空入力 の場合、他のバリデーションをスキップするという記述になります。

ここまで書いて、再び空投稿すると、

Image from Gyazo

このようにエラーが表示できるようになります。

ただし、フォームも消えてしまいました。

これは View 側に表示されるフォームに条件式を入れていた為です。

Image from Gyazo

バリデーションエラーが起こった場合、次の url にリクエストしていないので、アクションは create アクションにとどまっているためです。

ですので、View側の方にも対策をしておきましょう。

app/views/_form.html.erb【確認】

<div class="row">
  <div class="col-md-6">
    <%= form_with(model: task) do |form| %>
      <% if task.errors.any? %>
        <div style="color: red">
          <h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>

          <ul>
            <% task.errors.each do |error| %>
              <li><%= error.full_message %></li>
            <% end %>
          </ul>
        </div>
      <% end %>
      <% if params[:action] == "new" || params[:action] == "create" %>
        <div class="mb-3">
          <%= form.label :title, style: "display: block" %>
          <%= form.text_field :title, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :description, style: "display: block" %>
          <%= form.text_area :description, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :start_time, style: "display: block" %>
          <%= form.datetime_field :start_time, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :end_time, style: "display: block" %>
          <%= form.datetime_field :end_time, class: "form-control" %>
        </div>
      <% end %>
      <% if params[:action] == "edit" %>
        <div class="mb-3">
          <%= form.label :status, style: "display: block" %>
          <%= form.number_field :status, class: "form-control" %>
        </div>
      <% end %>

      <div class="mb-3">
        <%= form.submit class: "btn btn-primary" %>
      </div>
    <% end %>
  </div>
</div>
<% if params[:action] == "new" %>

この部分を以下のように書き換えます。

<% if params[:action] == "new" || params[:action] == "create" %>

app/views/_form.html.erb【編集】

<div class="row">
	<div class="col-md-6">
		<%= form_with(model: task) do |form| %>
		<% if task.errors.any? %>
		<div style="color: red">
			<h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>

			<ul>
				<% task.errors.each do |error| %>
				<li><%= error.full_message %></li>
				<% end %>
			</ul>
		</div>
		<% end %>
        <!-- この部分を書き換え -->
		<% if params[:action] == "new" || params[:action] == "create" %>
        <!-- この部分を書き換え -->
		<div class="mb-3">
			<%= form.label :title, style: "display: block" %>
			<%= form.text_field :title, class: "form-control" %>
		</div>
(以下略) 

こうする事によって、フォームが消える現象を防ぐ事ができます。

Image from Gyazo

2. Enumでステータスを管理する

次にステータスの表示です。

Enum

Enum とは、列挙型 と言われ、Railsでフラグ管理等に用いる integer 型の数値を管理しやすい Symbol 型のkeyと Integer 型のvalue を持った Hash にする事でメンテナンスをしやすくする仕組みです。

まずは、管理しにくい現状を確認してみましょう。

データーの初期化

新たにデーターが入っていない状態から始めたいので、一旦DBを初期化します。

todo_app % rails db:migrate:reset

と実行してDBを一旦作り直しましょう。

 todo_app % rails db:migrate:reset
Dropped database 'storage/development.sqlite3'
Database 'storage/test.sqlite3' does not exist
Created database 'storage/development.sqlite3'
Created database 'storage/test.sqlite3'
== 20231027073408 CreateTasks: migrating ======================================
-- create_table(:tasks)
   -> 0.0012s
== 20231027073408 CreateTasks: migrated (0.0013s) =============================

リセットできたようです。続いて、

todo_app % bin/dev

でサーバーを起動し、 localhost:3000 にアクセスします。

Image from Gyazo

New task からデーターを入力します。

Image from Gyazo

表示の不便さ

登録出来たら、 show アクションを読み込みますので、

Image from Gyazo

このような画面となります。 urllocalhost:3000/tasks/1 となっています。

この Status0 とされている所もDBの制約で、「デフォルト値を0」とするようになっている為です。

本当は、 0未着手 としたい目論見があるのですが、現状では 0 って何?という感じになっています。全く意味がわかりませんね。

明らかに変換が必要なので、その手法として今回は Enum を使っていきます。

フォームの不便さ

次に、このTaskの進捗を変更する場合、

Image from Gyazo

Edit this task を押下して、編集画面に遷移します。

Image from Gyazo

こちらのフォームも実際の入力に耐えないものとなっていますね。

Image from Gyazo

マイナスの値も自由に入れられていますので、これは改善の必要があります。

希望としては、セレクトボックス等で、 未着手 着手中 完了 などのいくつかのステータスを 選択 出来るようになれば良いですね。

実装の流れは、下記表のように変換されますが、

DB上のフラグの数値 数字を置き換える為のキー キーを日本語化したもの
0 :not_started 未着手
1 :in_progress 進行中
2 :closed 完了

段階を追って変換していこうと思います。

Model

では、早速置き換えるために Enumの記述を Task モデルに実装していきます。

Railsでは基本的に、こういったロジック関連は Model に書きます。

現状 app/models/task.rb

app/models/task.rb【確認】

class Task < ApplicationRecord
  validates :title, presence: true, length: { minimum: 3, maximum: 20 }
  validates :description, allow_blank: true, length: { minimum: 3, maximum: 500 }
  validates :start_time, presence: true
end

このようになっています。

では、書いてみましょう。

app/models/task.rb【追記】

class Task < ApplicationRecord
  enum status: { not_started: 0, in_progress: 1, closed: 2 } #追記部分

  validates :title, presence: true, length: { minimum: 3, maximum: 20 }
  validates :description, allow_blank: true, length: { minimum: 3, maximum: 500 }
  validates :start_time, presence: true
end

まず、 enum の宣言をし、 引数のキーにカラム名である :status そしてその値に { not_started: 0, in_progress: 1, closed: 2 } というHashが与えられています。

{ not_started: 0, in_progress: 1, closed: 2 }

このHashが置き換える数値と値の関係を定義しています。

これで第一段階が完了です。

View

todo_app % bin/dev

でサーバーを起動しましょう。

Image from Gyazo

先程 0 だった status のフラグが、 not_started に変わっていますね。

無事、対応するキーが割り当てられた形となります。

FormHelper

では、次に編集画面も見ていきましょう。

app/views/_form.html.erb【確認】

<div class="row">
  <div class="col-md-6">
    <%= form_with(model: task) do |form| %>
      <% if task.errors.any? %>
        <div style="color: red">
          <h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>

          <ul>
            <% task.errors.each do |error| %>
              <li><%= error.full_message %></li>
            <% end %>
          </ul>
        </div>
      <% end %>
      <% if params[:action] == "new" %>
        <div class="mb-3">
          <%= form.label :title, style: "display: block" %>
          <%= form.text_field :title, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :description, style: "display: block" %>
          <%= form.text_area :description, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :start_time, style: "display: block" %>
          <%= form.datetime_field :start_time, class: "form-control" %>
        </div>

        <div class="mb-3">
          <%= form.label :end_time, style: "display: block" %>
          <%= form.datetime_field :end_time, class: "form-control" %>
        </div>
      <% end %>
      <% if params[:action] == "edit" %>
        <div class="mb-3">
          <%= form.label :status, style: "display: block" %>
          <%= form.number_field :status, class: "form-control" %>
        </div>
      <% end %>

      <div class="mb-3">
        <%= form.submit class: "btn btn-primary" %>
      </div>
    <% end %>
  </div>
</div>

ここで少し独特な記法について見ていきます。

フォームを担っている部分は、それぞれ scaffold によって自動生成されており、

<%= form_with(model: task) do |form| %>
<% end %>

この form_with のブロック内の 引数 form をレシーバーとして使える formヘルパー の記法である

<%= form.text_field :title, class: "form-control" %>

<%= form.text_area :description, class: "form-control" %>

<%= form.datetime_field :start_time, class: "form-control" %>

<%= form.number_field :status, class: "form-control" %>

これらの部分によって書き込みが出来るようになっています。ざっくり

ヘルパーメソッド 用途
text_field 文字列の入力
text_area 改行を許容する文字列の入力
datetime_field 日付と時間の入力
number_field 数値の入力

このような目的のフォームとして指定されています。

ただし、今回使いたいフラグの選択肢に number_field はふさわしくありません。

セレクトボックスを生成する select を使ってみたいと思います。

書き換える部分は、

app/views/tasks/_form.html.erb【確認】(*コードが長いため一部のみを取り出しています。)

(略)
      <% if params[:action] == "edit" %>
        <div class="mb-3">
          <%= form.label :status, style: "display: block" %>
          <%= form.number_field :status, class: "form-control" %>
        </div>
      <% end %>
(略)

この edit 時に表示させる部分の

<%= form.number_field :status, class: "form-control" %>

この行です。

これを select を使って書き換えます。

記法としては、

<%= form.select :カラム名, データーとして入力される要素 %>
<%= form.select :status, [0, 1, 2] %>

このように書くと数値を直接入力できます。

Image from Gyazo

ただし、このまま更新しようとすると、

Image from Gyazo

status の値がエラーとなってしまいます。

データーとして入力される要素 には enum の記法に従う必要があります。

<%= form.select :status, [:not_started, :in_progress, :closed] %>

このように Enum のキーだけを渡すと、

Image from Gyazo

表示上都合よく整えつつ、データーを送る事ができます。

また、この記述は動的に書くべきですので、

<%= form.select :status, Task.statuses.keys %>

となります。

さらに、Bootstrapを適用するには少し癖があって、

<%= form.select :status, Task.statuses.keys, {}, { class: "form-control" } %>

と書く必要があります。

select のオプションは、書く場所と数を指定されているので、機能別に{} で括ってあげないと認識できず、

<%= form.select :カラム名, Enumに設定したキーの配列, { フォームの機能オプション }, { HTMLのオプション } %>

このような記法となります。

最終的に、

app/views/tasks/_form.html.erb【編集】

(略)
      <% if params[:action] == "edit" %>
        <div class="mb-3">
          <%= form.label :status, style: "display: block" %>
          <%= form.select :status, Task.statuses.keys, {}, { class: "form-control" } %>
        </div>
      <% end %>
(略)

このように書き換えが出来ていると良いでしょう。

すると、

Image from Gyazo

Bootstrapのフォームが適用できました。

Image from Gyazo

データーの編集も出来たようです。

3. git

git

ここまでを保存しておきましょう。

todo_app % git add .
todo_app % git commit -m "statusをenumへ"

GitHub

Githubの設定ができているのであれば、同名のリポジトリを作成し、pushしておきましょう。

おわりに

本チャプターはここまでとなります。

次回は

  • i18n
  • enum_help

を学びます。

ここまでお疲れ様でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?