1
1

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 3 years have passed since last update.

rails6で超シンプルなCRUDの実装

Last updated at Posted at 2021-08-08

はじめに

railsの環境構築で苦戦しどうにかこうにかrailsのトップページが表示できた後、達成感と同時に「で、どうやってWebアプリ作ればいいの?」という気持ちになりましたので、CRUD実装までをまとめました。

前提

本記事ではrails環境の構築は取り上げません。
database.ymlの設定なども済んでいる状態で進めます。

環境


$ sw_vers
ProductName:	macOS
ProductVersion:	11.4
BuildVersion:	20F71
$ ruby -v
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
$ rails -v
Rails 6.1.4
$ rbenv -v
rbenv 1.1.2
$ ruby-build --version
ruby-build 20210611
$ mysql --version
mysql  Ver 8.0.25 for macos11.3 on x86_64 (Homebrew)
 

概要

超シンプルなTodoアプリを作ります。(というかただただDBに対してCRUDの処理を行うだけです。)
本当にCRUDのところだけですのでログイン機能なども作りません。

  • 設計
  • 準備
    • model
    • view
    • controller
  • CRUDの実装
    • Read
    • Create
    • Update
    • Delete

設計

今回やるTodoアプリで実装するCRUDの処理は以下の通り
Create:Todoを作成する処理
Read:Todoを表示する処理
Update:Todo内容を変更する処理
Delete:Todoを削除(完了)する処理

それぞれDBを操作するSQL文に置き換えるとこう
C:Todoの作成 => INSERT文
R:Todoの表示 => SELECT文
U:Todo内容の変更 => UPDATE文
D:Todoの削除 => DELETE文

完成図はこう

クライアントのリクエストに対してレスポンスを返すという基本的な流れは左側の部分。
DBに対して何かしらの操作(CRUD)が必要な場合はControllerがModelを呼び出してグレーの部分の処理を行います。

実装

CRUDの前にまずは、クライアントからのリクエストに対して静的なページを返すところまで作成します。
図にするとこう。グレーのところは通りません。

Viewの作成

Viewの作成ではindex.html.erbの作成/todo_controller.rbの作成/routes.rbの編集を行います。
要はユーザのアクセスに対して適切なhtmlを表示するためのに必要なものをここで作ります。

index.html.erbの作成

まずはこのコマンドを実行します。 ※以降出てくるコマンドは全てrails newで作成または実行したディレクトリの直下で実行しています。

$ rails g controller todo

これを実行することでhtmlを作成するためのフォルダがapp/view/配下に作成されます。(Controllerも作成されますが、編集するのは次です。)
app/view/todo 配下に表示するhtmlファイルを作成します。

$ touch app/views/todo/index.html.erb

railsでhtmlを書くときはerbという拡張機能を用いて書きます。(haml、slimもありますが今回はerbを使います。)
通常のhtmlと何が違うかというとざっくり以下の通り。

  • 拡張子が違う
    html.erbという拡張子になっています。

  • htmlの中にrubyの記述ができる。
    htmlはそのまま使うと決まった内容しか返せませんが、html.erbだと特殊なタグで囲むことによってrubyを記述することができます。
    つまり、controllerで処理した結果を変数で表示したりDBの内容をループして表示したり、if文を用いた条件分岐で表示させる内容を切り替えたりできます。
    まず最初はviewを表示することを考えましょう。その後にerbの記述をしていきます。

index.html.erb
<h1>TOPページ</h1>

これだけです。
通常のhtmlで記載するhtmlやbodyタグは?と思われるかと思いますが、これだけでOKです。
(application.html.erbとyeldというタグについて調べると理解できるかと思います。)
以上でindex.html.erbの作成は完了です。

controller_todo.rbの作成と編集

index.html.erbを作成しただけではrailsはそのファイルをユーザにレスポンスとして返してはくれません。
Controllerにindex.html.erbを返す処理を記載する必要があります。
Controllerは先程の「rails g controllerコマンドで作成されていて app/controllers/配下にtodo_controller.rb
があります。
ここに先程作成したindex.html.erbを表示する処理を記述します。
記載の内容は以下の通り

app/controllers/todo_controller.rb

class TodoController < ApplicationController
    
    def index #追記

    end #追記

end


indexというメソッドを記述するだけです。内容はなくてもOKです。
こうすることでviewフォルダ配下のindex.html.erbを返してくれます。

routes.rbの編集

これでindex.html.erbを表示する処理がかけましたが、まだユーザはindex.html.erbにたどり着くことはできません。
なぜなら、todo_controller.rbのindexメソッドにアクセスするためのルートがないからです。
ルートはconfig配下のroutes.rbに記載します。

config/routes.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html

  get '/todo', to: "todo#index", as: "top"  #追記

end


こうすることで、http://[railsのip]:[port]/にgetリクエストをした時にtodo_controller.rbのindexメソッドにルーティングされます。routes.rbの書き方は他にもあるので調べてみてください。
以下のコマンドで現在のルートの確認ができます。

$ rails routes

(結果一部抜粋)
Prefix           Verb         URI Pattern        Controller#Action
top              GET           /todo                 todo#index

ここに先程追加した内容が表示されればOKです。

ここまでで

作成したindex.html.erbが表示されればほとんどできたも同然です。
作成したページにアクセスしてみます。
まずはサーバのスタート

$ rails s

ローカルホスト3000ポートにアクセスして先程作成したindex.html.erbが表示されればOKです。
http://localhost:3000/todo

ユーザのリクエストに対して適切なレスポンスを返すという流れが1つ出来上がっています。 ここからは、この流れにCreate/Read/Update/Deleteのリクエストに対して、適切なレスポンスを返す処理を作っていきます。

Read処理の実装

Read処理でやることは、DBからデータを取得して取得したデータをindex.html.erb内に含めてユーザに返すことです。
実際にはDBの中のtableに対してSELECT文を実行して値を取得しています。
図にするとこう.

DBからデータを取得する処理はtodo_controller.rbの中でModelのインスタンスを作成しデータを取得します。

Modelの作成

Modelの作成ではtodo.rb/migrateファイル/database/tableを作成します。
Modelの作成は以下のコマンドで行います。

$ rails g model Todo title:string comment:string limit:date

このコマンドを実行するとapp/models/配下にtodo.rbとdb/migrate配下にXXXX_create_todos.rbファイルが作成されます。
todo.rbはActiveRecordというクラスを継承しておりDBとのやりとりを円滑に行うためのメソッドを使えます。
XXXX_create_todos.rbはテーブルの情報をrubyの記述で書いたものです。これをコマンドでマイグレートするとXXXX_create_todos.rbで定義したテーブルが作成できます。

DBの準備

以下のコマンドでdatabase.ymlに記載されている情報をもとにDBを作成します。

$ rails db:create

さらに以下のコマンドを実行することで先程作成したXXXX_create_todos.rbをマイグレートし、DBないにテーブルを作成します。

$ rails db:migrate

完了したらRead処理のために何か適当なデータを投入しましょう(INSERT、irb、SEEDなど、投入もとはなんでも)
今回準備したデータは以下の通り

todos
mysql> select * from todos;
+----+-------+------------+------------+----------------------------+----------------------------+
| id | title | comment    | limit      | created_at                 | updated_at                 |
+----+-------+------------+------------+----------------------------+----------------------------+
|  1 | test1 | test-todo1 | 2021-08-10 | 2021-08-07 14:10:00.736786 | 2021-08-07 14:10:00.736786 |
|  2 | test2 | test-todo2 | 2021-08-10 | 2021-08-07 14:10:15.833124 | 2021-08-07 14:10:15.833124 |
|  3 | test3 | test-todo3 | 2021-08-10 | 2021-08-07 14:10:22.733875 | 2021-08-07 14:10:22.733875 |
|  4 | test4 | test-todo4 | 2021-08-10 | 2021-08-07 14:10:30.992338 | 2021-08-07 14:10:30.992338 |
|  5 | test5 | test-todo5 | 2021-08-10 | 2021-08-07 14:10:37.590373 | 2021-08-07 14:10:37.590373 |
+----+-------+------------+------------+----------------------------+----------------------------+
5 rows in set (0.00 sec)

DBからデータを取得する

todo_controller.rbのindexメソッド内に以下を記述します。

app/controllers/todo_controller.rb
class TodoController < ApplicationController
    
    def index
        @todos = Todo.all() #追記
    end

end

先程作成したtodo.rbで定義されているTodoというクラスのallというメソッドを用いてtodosテーブルのデータを全て取得して@todosという変数に格納しています。
@のついた変数はインスタンス変数を示す記号でインスタンスが存在する間有効な変数です。
これを指定することでこの変数をindex.html.erb内で扱うことができます。
1つのテーブルにつき1つのModelが対応していて、railsにおいてテーブルとやりとりをする場合このModelを通して行います。
Controllerの記述は以上。

index.html.erbに取得したデータを表示させる。

@todosはtodo_controller.rbでTodoクラスのallメソッドを用いて取得したデータです。
todosテーブルから取得した全てのレコードが配列として格納されています。配列の要素一つ一つはTodoクラスのインスタントなっておりtodosテーブルの1行分のデータを持っています。
この格納されたインスタンスたち(テーブルの1行ずつ)をeach文で回しそれぞれ必要なカラムのデータを表示します。

コードはこう

app/view/index.html.erb

<h1> TOPページ </h1>

<% @todos.each do | todo |%>

    <ul>

        <li><%= todo.title %></li>
        <li><%= todo.comment %></li>
        <li><%= todo.limit %></li>
        
    </ul>

   <h1>----------</h1>

<% end %>

erbの詳細は省きますが、<% %>で囲まれているところはrubyの処理が書いてあります。
今回はeach文で繰り返し処理をしているので<% end %>までの記述が繰り返されています。
<%= %>で囲まれているところもrubyの処理が書いてあります。先程との違いはhtmlに表示するかしないかです。
DBから取得したTodoの内容はブラウザに表示させたいので<%= %>で囲んでいます。
eachやifの様な制御構文はブラウザに表示させる必要がないので<% %>で囲みます。

結果はこう
スクリーンショット 2021-08-08 8.48.40.png

ちゃんとDBの内容が表示されているのがわかります。
ブラウザからhtmlのソースを見てみると
ちゃんと<% @todos.each do | todo | %> から<%= end %>までで囲んだ箇所が繰り返されているのがわかります。
(index.html.erbには1つ分しか書いていないのに、ブラウザのソースでは取得した分ちゃんと書かれている。)

ここまでやると

ここまでやった時の図はこうです

文章にするとこう

  • clientがhttp://localhost:3000の"/todo"にGetリクエストを送った時、routes.rbによって
    todo_controller.rbのindexメソッドが実行されます。
  • indexメソッドは、Todoインスタンスのallメソッドを実行しtodoテーブルから全てのデータを取得しindex.html.erbに渡すためのインスタンス変数@todosに格納します。
  • index.html.erbは受け取った@todosの中のデータをeachで順番に表示します。

Read処理は以上

Create処理の実装

Create処理でやることはindex.html.erbで入力フォームの作成/todo_controller.rbでcreateメソッドの作成/route.rbの編集です。
実際にはDBに対してINSERT INTO分が実行されています。

Create用ルートの編集

route.rbを編集します。
先程index.html.erbのform_withで指定したurlを作成してあげます。
今回はデータの追加を行うpostメソッドを指定します(httpメソッドについて詳細は省きます)

config/route.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  
  get '/todo', to: "todo#index", as: "top"  
  post '/todo', to: "todo#create", as:"create" #追記

end

Controllerでの処理を作成

todo_controller.rbではindexメソッドへの追記とcreateメソッド、todo_paramsメソッドの作成を行います。

app/controllers/todo_controller.rb

class TodoController < ApplicationController
    
    def index
        @todos = Todo.all()
class TodoController < ApplicationController
    
    def index
        @todos = Todo.all()

        # Viewのformで使う空のTodoインスタンスを作成します。
        # Viewはこのインスタンスにユーザから入力された値を入れてControllerに渡します。
        @new_todo = Todo.new #追記
    end


    ### 以下追記 ###
    def create
        # Viewからの入力を受け取りインスタンスを作成します。
        @todo = Todo.new(todo_params)
       
        # DBに値を追加した時の結果よるアクションを返します。
        # 今回は成功しても失敗しても画面を更新するだけです。
        respond_to do |format|
          if @todo.save
            format.html {redirect_to request.referer}
          else
            format.html {redirect_to request.referer}
          end
        end

    end

    private

    # StorongParameterと言ってセキュリティに関係するメソッド
    # todoモデル(:todo)で入力を許可するカラムを記載する。これ以外のデータは受け付けない様になる。
    def todo_params
        params.require(:todo).permit(:title, :comment, :limit)
    end

end

以上でtodo_controller.rbの編集は完了

Create用にViewの編集

入力フォームを作成するときはform_withを使います。
index.html.erbの先頭に次のコードを追記

app/view/todo/index.html.erb
<h1> TOPページ </h1>


<%# todo_controller.rbで定義した空のインスタンスに値を格納しcreate_pathにPOSTします。 %>
<%= form_with model: @new_todo, url:create_path do |form| %>
    <%= form.label :title %>
    <%= form.text_field :title%>

    <%= form.label :comment %>
    <%= form.text_field :comment %>

    <%= form.label :limit %>
    <%= form.date_field :limit %>

    <%= form.submit %>
<% end %>
(省略)

Createの確認

ここまでやったらブラウザの表示はこう

ちゃんと入力フォームができています。

この様にデータを入力してCreateTodoをクリックすると画面最下部に作成したTodoが追加されています。
ちなみにReadで取得したDBの値は取得した順番で並んでいます。新しいものを先頭にしたい場合は並べ替える必要があります。

id9のデータ として追加されています。(テストしている時にid6,7,8と余計なデータを作成していますが気にしないでください)

ここまで

ここまでやって時の図はこうです。

文章にするとこうです。

Createの実装は以上

Updateの実装

Update処理でやることは index.html.erbで更新のリンク作成/todo_controller.rbでupdateメソッドの作成/ route.rbでルートの編集です。
実施にはDBに対してUPDAET文が実行されます。

Update用ルートの編集

route.rbに追記します。
今回はデータの更新を行うpatchメソッドを指定しします。
宛先はこの後作成するtodo_controller.rbのupdateメソッドを指定します。

config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html

  get '/todo', to: "todo#index", as: "top"  
  post '/todo/create', to: "todo#create", as:"create" 
  patch '/todo/update', to: "todo#update", as:"update" #追記

end

Controllerでupdateメソッドを作成

todo_controller.rbに以下のメソッドを追記します。

app/controllers/todo_controller.rb

    def update
        ## viewから変更対象のデータのIDを受け取る
        todo_id = params[:id]

        ## todosテーブルから受け取ったIDと一致するインスタンス(行)を取得
        @update_todo = Todo.find(todo_id)    

        ## 取得したインスタンスのcomment(取得した行のcommentカラム)に"完了"を格納
        @update_todo.comment = "完了"

        ## 実行した時の処理を記載(createと同じ)
        respond_to do |format|
            if @update_todo.save
                format.html {redirect_to request.referer}
            else
                format.html {redirect_to request.referer}
            end
        end

    end

Update用にViewを編集

app/views/todo.index.html.erb
<h1> TOPページ </h1>

<%= form_with model: @new_todo, url:create_path do |form| %>
    <%= form.label :title %>
    <%= form.text_field :title%>

    <%= form.label :comment %>
    <%= form.text_field :comment %>

    <%= form.label :limit %>
    <%= form.date_field :limit %>

    
    <%= form.submit %>
<% end %>

<% @todos.each do | todo |%>

    <ul>
        <li>id<%= todo.id%>のデータ</li>
        <li><%= todo.title %></li>
        <li><%= todo.comment %></li>
        <li><%= todo.limit %></li>

        <%# 以下の一行を追加してリンクを表示 %>
        <%= link_to "更新", update_path(id: todo.id), method: :patch %>
        
    </ul>

    <h1>----------</h1>

<% end %>

追加した行により、さきほどroute.rbに追加したupdate_pathにpatchリクエストを送信します。
その際にidというパラメータを一緒に送信します。これにはtodo.idの値(表示しているデータのid)が入っていて、どのデータを更新するのか判別するために送信しています.
todo_controller.rbのupdateメソッド側ではtodo_id = params[:id]のところで受け取っています。

Updateの確認

ここまでやるとブラウザの表示はこう
スクリーンショット 2021-08-08 9.15.37.png
更新というリンクが各データの下に着きましたね。

クリックするとこう
スクリーンショット 2021-08-08 9.16.28.png

コメントを表示している箇所が"完了"に更新されるかと思います。

ここまで

ここまでやった時の図はこうです

文章にするとこうです

  • index.html.erbの"更新"リンクをクリックするとhttp://localhost:3000/todo/updateにPATCHリクエストが送信され、routes.rbによってtodo_controller.rbのupdateメソッドが実行されます。
  • updateメソッドはindex.html.erbから受け取ったidをもとに更新対象のでーたをtodoテーブルから探します。
  • updateメソッドは対象のデータの中のcommentカラムのデータを"完了"に書き換え保存し、画面更新を行います。
  • 画面更新により再びhttp://localhost:3000/todoにGETリクエストが送信されます。その時もtodo_controller.rbのindexメソッドが実行されtodoテーブルのデータが改めて取得されるため先程更新したデータが画面に表示されてきます。

Updateの実装は以上

Deleteの実装

CRUDの最後になります。
Delete処理でやることは、index.html.erbで削除用のリンク作成/todo_controller.rbでdeleteメソッドの作成/ route.rbでルートの編集です。
お気づきかもしれませんがUpdate処理と同じです。
todo_controller.rbで行う処理が少し違うだけですのでサラッと

Delete用ルートの編集

config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html

  get '/todo', to: "todo#index", as: "top"  
  post '/todo/create', to: "todo#create", as:"create" 
  patch '/todo/update', to: "todo#update", as:"update" 
  delete '/todo/delete', to: "todo#delete", as:"delete" #追記
end

特に補足することはありませんね。

Controllerでupdateメソッドを作成

app/controllers/todo_controller.rb

    def delete
        ## viewから変更対象のデータのIDを受け取る
        todo_id = params[:id]

        ## todosテーブルから受け取ったIDと一致するインスタンス(行)を取得
        @delete_todo = Todo.find(todo_id)    

        ## 削除の実行実行と実行した時の処理を記載
        respond_to do |format|
            if @delete_todo.destroy
                format.html {redirect_to request.referer}
            else
                format.html {redirect_to request.referer}
            end
        end

    end

updateでは@update_todo.comment = "完了"として値を変更していましたが、
今回はdestroyメソッドを利用して対象のデータを削除します。

Update用にViewを編集

app/views/index.html.erb
<h1> TOPページ </h1>

<%# todo_controller.rbで定義した空のインスタンスに値を格納しcreate_pathにPOSTします。 %>

<%= form_with model: @new_todo, url:create_path do |form| %>
    <%= form.label :title %>
    <%= form.text_field :title%>

    <%= form.label :comment %>
    <%= form.text_field :comment %>

    <%= form.label :limit %>
    <%= form.date_field :limit %>

    
    <%= form.submit %>
<% end %>

<% @todos.each do | todo |%>

    <ul>
        <li>id<%= todo.id%>のデータ</li>
        <li><%= todo.title %></li>
        <li><%= todo.comment %></li>
        <li><%= todo.limit %></li>

        
        <%= link_to "更新", update_path(id: todo.id), method: :patch %>
        <%# ここに削除のリンクを追加 %>
        <%= link_to "削除", delete_path(id: todo.id), method: :delete %>
        
    </ul>

    <h1>----------</h1>

<% end %>

更新用のリンクと同じくリンクを作成します。
先程route.rbにdeleteメソッドを指定しているためdeleteメソッドを指定します。
また、削除対象のデータを探すためにidを渡します。

Deleteの確認

ここまでやるとブラウザの表示はこう

スクリーンショット 2021-08-08 9.47.31.png

削除のリンクが表示されていますね

クリックするとこう
id1のデータが削除されます
スクリーンショット 2021-08-08 9.51.53.png

ここまで

ここまでやった時の図はこうです。

文章にするとこう

  • index.html.erbの"削除"リンクをクリックするとhttp://localhost:3000/todo/deleteにDELETEリクエストが送信され、routes.rbによってtodo_controller.rbのdeleteメソッドが実行されます。
  • deleteメソッドはindex.html.erbから受け取ったidをもとに更新対象のでーたをtodoテーブルから探します。
  • deleteメソッドは対象のデータを削除します。
  • 画面更新により再びhttp://localhost:3000/todoにGETリクエストが送信されます。その時もtodo_controller.rbのindexメソッドが実行されtodoテーブルのデータが改めて取得されるため先程更新したデータは画面からきえています。

Deleteの実装は以上です
これで超シンプルなCRUD処理の実装は完了です。

最後に

長くなりましたが、以上でCRUD処理の実装は完了です。
冒頭にも申し上げた通り、超シンプルなCRUDの処理だけを実装したのでWebアプリとして使える様なものではありません。しかしWebアプリの骨になる部分はこれで作れる様になるかと思います。
あとは、ログイン機能をつけてユーザごとに異なる情報を表示したりとか、ユーザがデータの入出力をしやすい様にとか、外部のサービスと連携して便利な機能を使うとかを肉付けしていく様に勉強していくのが良いのかなーと個人的に思っています。
ここまで読んでくださった方がいらっしゃいましたら嬉しい限りです。ありがとうございます。

1
1
1

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?