Ruby on RailsでToDoリストアプリ作成②サンプルデータ投入・ToDoリストの表示

環境
  • Windows11
  • ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x64-mingw-ucrt]
  • Rails 8.0.0

本記事の内容

  1. ToDoリストモデルの作成
  2. ToDoリストテーブルの作成
  3. ToDoリストのサンプルデータ投入
  4. ToDoリスト表示ページの作成
  5. ToDoリストのデータ取得⇒表示

モデルの作成

モデルの作成はコマンドから。

項目(カラム)の定義に必要な型の一覧は下記の通り。

指定できる型概要値の範囲
primary_key主キー制約
string文字列型255
text文字列型65,535(?)
integer整数型-2147483648
~
2147483647
float小数点型8 bytes
decimal浮動小数点型8 bytes(?)
datetimeデートタイム型(?)
timestampタイムスタンプ型
time時刻型
date日付型
binaryバイナリ型
boolean真偽型true
or
false

ちょっとまだわかっていない部分があるので、その辺は実装の中で判明した時に追記します。

今回作成するモデルに必要な項目をまとめます。(後ほど変わります

項目名カラム名データ型
作成日時created_timetime
期限日時limit_timetime
メモ(内容)memostring
優先度priorityinteger

日時の項目に関しては、以下の記事を参考にTime型を選びました。(後ほど変わります

それに合わせてカラム名も「○○_time」にしています。(Dateだったら_date)

モデル作成用コマンドは下記のとおりです。

$ rails g model ToDo \
    created_time:time \
    limit_time:time \
    memo:string \
    priority:integer

実行後ログは以下の通り。

invoke  active_record
create    db/migrate/20241209045344_create_to_dos.rb
create    app/models/to_do.rb
invoke    test_unit
create      test/models/to_do_test.rb
create      test/fixtures/to_dos.yml

モデルファイルの中身は以下の通り。

class ToDo < ApplicationRecord
end

項目が追加されるものだと思っていたんですが、どうやら違うようです。

次項で使用するマイグレーションファイルの中身は以下の通り。

class CreateToDos < ActiveRecord::Migration[8.0]
  def change
    create_table :to_dos do |t|
      t.time :created_time
      t.time :limit_time
      t.string :memo
      t.integer :priority

      t.timestamps
    end
  end
end

想定通りのDDLらしきものが出来上がりました。

最後のタイムスタンプは何なんだろう…。

テーブルの作成

テーブルの作成には、前項で作成したマイグレーションファイルを使用します。

マイグレーションを実行する前に、DBの状態を確認しておきます。

$ rails db
SQLite version 3.47.1 2024-11-25 12:07:48
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  schema_migrations 

テーブル一覧に、ToDoテーブルはありませんね。

それではマイグレーションの実行です。

$ rails db:migrate
== 20241209045344 CreateToDos: migrating ======================================
-- create_table(:to_dos)
   -> 0.0047s
== 20241209045344 CreateToDos: migrated (0.0049s) =============================

ToDoテーブルのCREATEログが流れているのが確認できます。

さっそくDBの状態を確認。

$ rails db
SQLite version 3.47.1 2024-11-25 12:07:48
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  schema_migrations     to_dos

ToDoテーブルが作成されていますね。

せっかくなので、カラムも確認してみます。

sqlite> .schema to_dos
CREATE TABLE IF NOT EXISTS "to_dos" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "created_time" time, "limit_time" time, "memo" varchar, "priority" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);

んー?

自動採番のIDは良いとして、意図しない作成日時(created_at)と更新日時(updated_at)があります。

しかもDateTime型…だと!?

ちょっと調整が必要そうです。

テーブル定義変更

コマンドによるモデル・テーブル作成における仕様が分かったところで、その動きに合わせて変更を加えていきます。

まずはモデルの項目から、変更後は以下の通りです。

項目名カラム名データ型
作成日時created_timetime
期限日時limit_timetime
datetime
メモ(内容)memostring
優先度priorityinteger

作成日時がデフォルトで入るので不要になりました。

また、デフォルトで入る項目の型がDateTimeとなっていたので、そちらに合わせました。(Timeの方が良いらしいですけどね!)

次にテーブルの修正ですが、まずはマイグレーションの状態を確認しておきます。

$ rails db:migrate:status

database: storage/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20241209045344  Create to dos

ToDoテーブルの作成が適用されていることが分かりましたので、ロールバックします。

$ rails db:rollback
== 20241209045344 CreateToDos: reverting ======================================
-- drop_table(:to_dos)
   -> 0.0047s
== 20241209045344 CreateToDos: reverted (0.0237s) =============================

次にマイグレーションファイルを修正します。

class CreateToDos < ActiveRecord::Migration[8.0]
  def change
    create_table :to_dos do |t|
      t.datetime :limit_time
      t.string :memo
      t.integer :priority

      t.timestamps
    end
  end
end

最後にマイグレーション実行。

$ rails db:migrate
== 20241209045344 CreateToDos: migrating ======================================
-- create_table(:to_dos)
   -> 0.0047s
== 20241209045344 CreateToDos: migrated (0.0051s) =============================

念のため、DBの状態も確認しておきます。

$ rails db
SQLite version 3.47.1 2024-11-25 12:07:48
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  schema_migrations     to_dos
sqlite> .schema to_dos
CREATE TABLE IF NOT EXISTS "to_dos" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "limit_time" datetime(6), "memo" varchar, "priority" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);

無事、テーブル定義の変更が完了しました。

サンプルデータ投入

テーブル定義が固まったところで、表示用のサンプルデータを投入したいと思います。

Railsでデータを投入するには、db/seed.rbに処理を追加するようです。

レコードごとにインサート処理を書くこともできるみたいですが、今回はCSVファイルにデータを定義していきます。

CSVファイルインポート用のコード(db/seed.rb)は下記の通り。

require "csv"

CSV.foreach('db/data/to_dos.csv') do |data|
  ToDo.create(limit_time: data[0], memo: data[1], priority: data[2])
end

CSVファイルの中身は以下の通り。

"2024-12-25 21:00","memo1",5
"2024-12-25 21:00","memo2",4
"2024-12-25 21:00","memo3",3
"2024-12-25 21:00","memo4",2
"2024-12-25 21:00","memo5",1

データ投入前に、データベースを綺麗にしておきます。

Windows環境では下記のコマンドが使えないみたいなので注意。

$ rails db:migrate:reset
Permission denied @ apply2files - C:/work/Ruby/ToDoListApp/storage/development.sqlite3
Couldn't drop database 'storage/development.sqlite3'
bin/rails aborted!
Errno::EACCES: Permission denied @ apply2files - C:/work/Ruby/ToDoListApp/storage/development.sqlite3 (Errno::EACCES)

Tasks: TOP => db:drop:_unsafe
(See full trace by running task with --trace)

代わりに以下のコマンドで、DBをクリーンアップします。(やや強引ですが…)

$ rm storage/development.sqlite3
$ rails db:migrate

クリーンアップが完了したので、いよいよデータ投入。コマンド一発です。

$ rails db:seed

ToDoテーブルを確認してみます。

$ rails db
SQLite version 3.47.1 2024-11-25 12:07:48
Enter ".help" for usage hints.
sqlite> select * from to_dos;
1|2024-12-25 21:00:00|memo1|5|2024-12-11 05:31:13.374140|2024-12-11 05:31:13.374140
2|2024-12-25 21:00:00|memo2|4|2024-12-11 05:31:13.405766|2024-12-11 05:31:13.405766
3|2024-12-25 21:00:00|memo3|3|2024-12-11 05:31:13.412657|2024-12-11 05:31:13.412657
4|2024-12-25 21:00:00|memo4|2|2024-12-11 05:31:13.428899|2024-12-11 05:31:13.428899
5|2024-12-25 21:00:00|memo5|1|2024-12-11 05:31:13.435013|2024-12-11 05:31:13.435013

無事にデータ投入が完了しました。

表示ページの作成

データ投入まで完了したところで、今度はそれらを表示する画面を作ってみます。

Railsでは画面(View)を作るにはまずControllerを作成する模様。

Controllerの作成コマンドにはオプションが存在するが、まずは最小で作成してみる。

$ rails g controller to_dos
      create  app/controllers/to_dos_controller.rb
      invoke  erb
      create    app/views/to_dos
      invoke  test_unit
      create    test/controllers/to_dos_controller_test.rb
      invoke  helper
      create    app/helpers/to_dos_helper.rb
      invoke    test_unit

作成されたControllerファイルを見てみる。

class ToDosController < ApplicationController
end

う~ん、シンプル。

作成時のログから分かる様に、Viewのフォルダは作成されたがViewファイルは未作成のまま。

よって次はViewファイルを作成する。

Viewファイルの命名規則は以下の通り。

アクション名.html.拡張子

アクション名とそれぞれの動作については下記を参考にした。

今回は表示の機能なので、indexが妥当。

よって、「views/to_dos/index.html.erb」を作成。

ページ遷移の動作確認時に分かるように、文字だけ入れておく。

<p>ページ遷移できたよ!</p>

Viewファイルを作成したところで、次に設定するのはルーティング。

Railsのルーティングは「config/routes.rb」に設定される。

アプリ作成直後のファイルの中身は以下の通り。

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
  # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
  # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker

  # Defines the root path route ("/")
  # root "posts#index"
end

これをパッと見て、アプリのフォルダを眺めて、「起動時にはいったいどんな動きであのページが表示されているんだ?」となった。

起動時のログは以下の通り。

Started GET "/" for 127.0.0.1 at 2024-12-16 10:35:48 +0900
  ActiveRecord::SchemaMigration Load (0.2ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='ToDoListApp'*/
Processing by Rails::WelcomeController#index as HTML
  Rendering C:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/railties-8.0.0/lib/rails/templates/rails/welcome/index.html.erb
  Rendered C:/Ruby33-x64/lib/ruby/gems/3.3.0/gems/railties-8.0.0/lib/rails/templates/rails/welcome/index.html.erb (Duration: 1.4ms | GC: 0.0ms)
Completed 200 OK in 1094ms (Views: 9.3ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)

Rubyに組み込まれているControllerが作動しているらしい?

ひとまず、この初期起動時の画面を作成したViewファイルに差し替えたい。

以下の通り、ルーティングの設定を変更する。

Rails.application.routes.draw do
  root to: "to_dos#index"
end

起動時のログは以下の通り。

Processing by ToDosController#index as HTML
  Rendering layout layouts/application.html.erb
  Rendering to_dos/index.html.erb within layouts/application
  Rendered to_dos/index.html.erb within layouts/application (Duration: 0.7ms | GC: 0.0ms)
  Rendered layout layouts/application.html.erb (Duration: 71.7ms | GC: 0.0ms)
Completed 200 OK in 1104ms (Views: 81.3ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)

上記を見ると分かるが、「views/layouts/application.html.erb」というのが親にいるらしい。

その親ファイルの中身は以下の通り。

<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "To Do List App" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="mobile-web-app-capable" content="yes">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= yield :head %>

    <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
    <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>

    <link rel="icon" href="/icon.png" type="image/png">
    <link rel="icon" href="/icon.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/icon.png">

    <%# Includes all stylesheet files in app/assets/stylesheets %>
    <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

何となく理解できる!

データ取得⇒表示

MVCモデルのそれぞれのファイルが作成できたところで、いよいよ結合していく。

データ取得

まずはDBに投入したサンプルデータをControllerで取得する。

全件取得はとても簡単で、「モデル.all」で出来ちゃいます。

class ToDosController < ApplicationController
  def index
    @todo = ToDo.all
    Rails.logger.debug @todo.inspect
  end
end

仕込んだログ出力の結果がコチラ。

#<ActiveRecord::Relation [#<ToDo id: 1, limit_time: "2024-12-25 21:00:00.000000000 +0000", memo: "memo1", priority: 5, created_at: "2024-12-11 05:31:13.374140000 +0000", updated_at: "2024-12-11 05:31:13.374140000 +0000">, #<ToDo id: 2, limit_time: "2024-12-25 21:00:00.000000000 +0000", memo: "memo2", priority: 4, created_at: "2024-12-11 05:31:13.405766000 +0000", updated_at: "2024-12-11 05:31:13.405766000 +0000">, #<ToDo id: 3, limit_time: "2024-12-25 21:00:00.000000000 +0000", memo: "memo3", priority: 3, created_at: "2024-12-11 05:31:13.412657000 +0000", updated_at: "2024-12-11 05:31:13.412657000 +0000">, #<ToDo id: 4, limit_time: "2024-12-25 21:00:00.000000000 +0000", memo: "memo4", priority: 2, created_at: "2024-12-11 05:31:13.428899000 +0000", updated_at: "2024-12-11 05:31:13.428899000 +0000">, #<ToDo id: 5, limit_time: "2024-12-25 21:00:00.000000000 +0000", memo: "memo5", priority: 1, created_at: "2024-12-11 05:31:13.435013000 +0000", updated_at: "2024-12-11 05:31:13.435013000 +0000">]>

少し見づらいですが、取れてますね。

データ表示(一覧表示)

データを取得できたところで、画面に表示してみましょう。

ひとまず表示するのはToDoの内容(memo)だけ。

Viewファイルを以下のように変更します。

<% @todo.each do |todo| %>
  <p><%= todo.memo %></p>
<% end %>

実行結果がコチラ。

さすがに味気なさすぎるので、少し見栄えを良くしていきましょう。

ViewファイルとCSS(assets/stylesheets/application.css)をそれぞれ次のように修正します。

<table>
  <tr>
    <th colspan="2">ToDo</th>
  </tr>
  <tr>
    <th>優先度</th>
    <th>内容</th>
  </tr>
  <% @todo.each do |todo| %>
    <tr>
      <td class="priority">
        <% if todo.priority == 5 %>
          最高
        <% elsif todo.priority == 4 %>
          高い
        <% elsif todo.priority == 3 %>
          普通
        <% elsif todo.priority == 2 %>
          低い
        <% elsif todo.priority == 1 %>
          最低
        <% end %>
      </td>
      <td><%= todo.memo %></td>
    </tr>
  <% end %>
</table>
table {
  width: 100%;
}

th {
  border: solid 1px #aaaaaa;
}

td {
  border: solid 1px #aaaaaa;
}

.priority {
  width: 20%;
  text-align: center;
}

実行結果がコチラ。

ひとまず、最低限の表示機能を持たせることが出来ました。

今回はここまで。次回以降、処理を追加していきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA