7
9

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.

とりあえずこれだけ。Rails ToDoアプリ作成【CRUD】勉強会 #1

Last updated at Posted at 2019-11-15

初心者用Railsアプリ練習帳

勉強会を開き、Rails初学者に日頃教えています。初心者向け勉強回資料用もかねて作成。
ある程度基礎を学習していると、読みながら真似はできるけど、いまいち全体の知識がつながっていないので自分で作ることができない…

そんな状態になりがちでした。

対象者

  • Railsでなんでもいいからの教材をやった人
  • Rails基礎はなんとなくわかってきた人
  • Railsで自分でアプリ作るのはまだ無理そうという人
  • Rails以外のフレームワークならわかる人

※ 正確さより概念とアプリ作成の流れを重視します。

コンセプト

他にも言っている方がいますが、勉強会で教えている経験から初学者が同じ教材を何周もするのは上達が遅いように感じています。

知識はあるけど自分で一から作るのは無理そう。
エラーが出て対処出来ない

ということになりがちでした。

こういう場合、実際に手を動かし、自分の頭で考えて簡単なアプリを作る必要があるけど、

  • いきなりだと何を作ればいいのかわからない…
  • 一連のアプリを作っていく順番がわからない…
  • エラーでても対処できない…
  • 簡単なのから作ってみればいいと言うが何が簡単なものなのか分からない…
  • 何個もアプリ自分で作るといいらしいが何を作れば練習やスキル向上なるか分からない…

そんな人が未経験からプログラミングをやる人には何人もいました。
現状の私の考えですが、世の中にある教材は見た目とか完成度ないと売れないし、強い人達からツッコまれるので分かりやすさより精確さが必要とされる教材になってしまうようにおもいます。

なので、後で身につければ良いこともあれもこれも付け足しており、本当にコレだけ!
という事項が初心者には見えにくくなっている気がします。

それをうけて今回、ムダなものとにかく剥ぎまくって、コアな部分だけを数多く練習すればみんな出来るようになるのではないかと考え、練習問題を作りました。
今後も感想とかもらって継続的にブラッシュアップしていければと思っています。

手順

  1. とりあえずToDoアプリをなぞって作ってみる(少しだけでも勉強した人が前提) ←今回はココ
  2. 超シンプルアプリを量産。手を動かして覚える!概念つかむ!
  3. データベースから欲しいデータを取得する!千本ノックのようにやりまくります!

2,3は個人的に開く勉強会で解説します。

とりあえずToDoアプリをなぞって作ってみる

環境構築はローカルでもいいですけど、WindowsやMacで違うので初心者はcloud9で作っていいと思います。
構築方法は今回省略

以下のコマンドでアプリを作成。プロジェクト名になります。なんでもいいです。
今回、私はrails-todo1にしました

$ rails new rails-todo1

沢山のファイルが作られる。終わったらプロジェクトのディレクトリに移動

$ cd rails-todo1

サーバーを起動させて表示させてみる

$ rails s
welcome_rails.png

こんな画面が出たらOK

これから中身を作っていくので一応流れを確認

今回新規作成で一から作っていきますが、だいたいこんな感じで作ると考えていただければよいかと思います。

新しいアプリ(ページ)を作る手順

全体の流れで考えること

  1. 作るページはどんなページで作る?
  2. データベースに保存するデータは何か?
  3. モデルは作ったか?
  4. マイグレーションファイルを生成&データベースに反映
  • generate modelコマンドするとmigrationファイルも作られる。
  • カラム(データベースの列)を追加したり、データベースの中身を変えたいときにもマイグレーションファイル使う
  • rails db:migrateでマイグレーションファイルの内容をデータベースに反映
  1. ページを表示するのか?データを登録・更新をするのか?
  • 表示するならGET
  • データの登録ならPOST
  • データの更新ならPATCH (PUT)
  • データの削除ならDELETE
  1. ルーティングを決める
  • URLにパラメータをいれる必要があるのか?(例えば各ユーザーの情報ページを表示するならparams[:id]のように書く。ならばURLに〇〇〇/:idのようにidを含める必要がある)
  • URLにパラメータいらない(新規登録や全データ表示など)
  1. 処理を行うコントローラーは作ったか
  2. コントローラーのアクションは作ったか
  • GET?POST?PATCH?DELETE?かを確認してつくる
  1. ビューファイルはいるか?

さっそく簡単なアプリ作成していく

1.どんなもの作るか

↓ なんの飾りもない超シンプルアプリ
image.png

テキストを入力して登録する
todo2.gif

2.データベースに保存するデータは何か

  • テーブル名:Todo
  • カラム名:title
  • デフォルトで作られるカラム(ID, 作成日, 更新日)

3. モデルは作ったか

今回は、まだ何もつくってないので当然必要。
今回作るToDoアプリでは、Todoテーブルのデータ取得・新規登録・データ更新・データ削除を行うのでModelを1つ作成します

というわけで、モデル作成。

$ rails generate model Todo title:string

2.で書いたように必要なテーブルがTodo
必要なカラムがtitleなので、このように書いています。

rails generate model Todoでモデルファイルが作られる
title:string部分は追加したいカラム名と型。
この部分は書かなくても手動で記述すれば作れるのですが、コマンドでこのように打ったほうが楽です

db/migrate/~~~~~~~~~_create_todos.rb
class CreateTodos < ActiveRecord::Migration[5.2]
  def change
    create_table :todos do |t|
      t.string :title

      t.timestamps
    end
  end
end

補足:モデルとは

普通はデータベースからデータを取得するときにはSQLというものを書かないといけなくて初心者にはそれなりに複雑。
Railsではmodelというものを使うとSQL書かなくてもデータ取得できて便利。

4. マイグレーションファイルを生成&データベースに反映

今回新規作成なのでモデルをつくるとmigrationファイルも作られるのでコマンドでmigrationファイルを作る必要ありません。

マイグレーションファイルの内容をデータベースに反映させるコマンド打つ

$ rails db:migrate

補足:migrationファイル作る必要あるときは

migrationファイルとは:
データベースのテーブルの作成、変更を記述するためのファイルで、rails db:migrateを実行することでデータベースに反映させる。

【コマンド詳細】カラムの追加。今回は実行必要しないでください
$ rails generate migration add_password_digest_to_users password_digest:string

こんな感じのコマンド入れてカラムの追加とかやります。
今回は当然新規必要なし。
実行するとmigrateでエラーになります。

5. ページを表示するのか?データを登録・更新をするのか?

→ 表示するならGET
→ データの登録ならPOST
→ データの更新ならPATCH (PUT)
→ データの削除ならDELETE

今回は最初のページが
image.png

Todoを追加するための機能を持つページを「表示」します。
ということでGETメソッドです

6. ルーティングを決める

ルーティングは
どのURLにアクセスすると、どのコントローラーとアクションを使うのか定義するものです。

Railsにはresourcesという便利機能があってルーティング書くのを便利にできる機能があるんですが、今回は原理を理解するために今回は一つ一つルーティングを記述していきましょう。

config/routes.rb
get '/todos', to: 'todos#index', as: 'todos'
post '/todos', to: 'todos#create'

ルーティングを確認する

コンソールに↓を入力するとルーティングを確認できる。

$ rails routes
Prefix  Verb  URI Pattern     Controller#Action
todos   GET  /todos(.:format) todos#index
        POST /todos(.:format) todos#create

もしくは
/rails/info/routesをアドレスバーにつけると少し見やすい画面になる
http://sample.com/rails/info/routesみたいな感じ
他タブで開いておくといちいちコマンド入れなくても確認できるので便利!

image.png

現状では/todosにGETを設定してtodoコントローラーindexアクションの処理を使えと指定している

補足:_pathについて

$ rails routesだとPrefixtodosという名前になっていてURLに名前を付けて便利に使えるようになっている。これに_pathを追加すると使えます。
todos + _pathtodos_pathです。

todos_pathと書くと/todosと書いたのと同じことになります。
結構使いますので覚えておくといいです。

7. 処理を行うコントローラーは作ったか

コントローラーは大雑把にルーティングモデルビューをつなぐ役割、データ返す役割をしているものです。

mvc_pattern.png

コントローラーは何をするか雑に説明

細かく言うとわかりにくくなるので大雑把に。

  • ルーティングで該当のURLにマッチしたら、コントローラーのアクションに処理が飛んでくる。そして処理を行う。
    • その際にparamsのデータもらうこともあり
  • Todo.new使ってビューファイルで記述するform_with(form_forも含む)で使うフォームでデータを送信するために必要なモデルの部品みたいなものが入った箱作ったりする。
    • ビューで使うならインスタンス変数に格納する
  • Todo.find(params[:id])みたいに自分で作ったModel使ってデータベースの値を取得する。
    • Todo.find(params[:id])Todo.newで作った部品に、取得したデータも含んだものだと思っておけばいいです。※下図参照
    • ビューで使うならインスタンス変数に格納する
  • インスタンス変数はビューで使用可能なのでページを操作によってデータ表示やフォームの送り先を変えられる動的なページを作れる

image.png

初心者のうちはデータ取得したり、データを加工する、ビューで表示するためのデータを@todoみたいなインスタンス変数を作る!
みたいな感じでいいと思います。

コントローラー作成コマンド

以下コマンドで、Controllerを作成します。

とりあえずコントローラーを生成する

$ rails g controller Todos

このコマンドならtodosというコントローラー作成されるので、todos_controller.rbというファイルができます。

8. コントローラーのアクションは作ったか

app/controllers/todos_controller.rb
class TodosController < ApplicationController
  def index
    @todos = Todo.all
  end
end

コントローラーの処理を書いてみました。
Todoテーブルに入っているデータを全件取得(allメソッドを使う)してインスタンス変数の@todosに入れています。

9. ビューファイルはいるか?

ビューファイルは表示部分です。
htmlっぽい書き方だけどコントローラーなどで処理したインスタンス変数などを使って静的なページでなく動的なページを作ることができます。

さきほど、@todosというインスタンス変数をコントローラーのindexアクションに書いたので、これを作って全データを表示してみましょう。

index.html.erbが存在しない場合はapp/views/todos/index.html.erbに作成しましょう。

app/views/todos/index.html.erb
<h1>ToDo一覧</h1>
<table>
  <thead>
    <tr>
      <th>タスク</th>
    </tr>
  </thead>
  <tbody>
    <% @todos.each do |todo| %>
      <tr>
        <td><%= todo.title %></td>
      </tr>
    <% end %>
  </tbody>
</table>

作成した画面

まだデータの登録がないので全件取得しても表示するものがないので、このような表示になっています。
image.png

こんな1~9までのような順序でアプリの作成(もしくは追加)をしていくことになります。

では、次にTodoの全件表示しかできない状態なので
次は新規ToDo追加できる機能を追加します

新規追加機能の作り方

次に新規登録できる機能を作ります。
今度は1~9まの順序で機能を追加していきましょう。

1.どんなもの作るか

ToDoテーブルにデータを新規追加する機能
テキストフィールドに入力した文字をボタン押すとデータベースに登録

image.png

2.データベースに保存するデータは何か

テーブル名:Todo
カラム名:title
デフォルトで作られるカラム(ID, 作成日, 更新日)

titleだけデータベースに保存すればいいことになります。

3. モデルは作ったか

モデルはTodoつかうので新しく作成の必要なし

4. マイグレーションファイルを生成&データベースに反映

モデルはTodo使う。カラムの追加も必要ないのでマイグレーションファイル作る必要も、データベースに反映する必要もない。

5. ページを表示するのか?データを登録・更新をするのか?

カラム名でtitleをデータベースに登録する処理を追加することになります。

ページの表示をしているわけでも、情報の更新をしているわけでもなく、情報の削除でもありません。

こういうデータの登録処理はPOSTで行いましょう。

6. ルーティングを決める

  • メソッド:POST
  • ルーティング:/tasks
  • コントローラー&アクションtasks#create

ルーティングを確認

実はすでに追加してしまっていたのですが、今回使うルーティングは↓の二つ目です。

routes.rb
Rails.application.routes.draw do
  get '/todos', to: 'todos#index', as: 'todos'
  post '/todos', to: 'todos#create'
end

ページを表示しないで新規登録を行うのでPOSTですね。

7. 処理を行うコントローラーは作ったか

新しいモデルを作ったわけでも、わざわざ新しいコントローラーをつくる必要もありません。
todoテーブルのToDoのデータ追加処理を追加するのでコントローラーはtodos_controller.rbに追記すればOKです。

8. コントローラーのアクションは作ったか

データ追加処理を記述したアクションをtodos_controller.rbに追記していきましょう。

コントローラーにアクションを追加

createを追加しました。新規登録を行うためのアクションになります。

app/controllers/todos_controller.rb
class TodosController < ApplicationController
  def index
    @todos = Todo.all
    @todo = Todo.new # フォーム作るために必要
  end
  
  def create
    @todo = Todo.create(todo_params)
    redirect_to todos_path
  end
end

redirect_to todos_path
としているのは、ページを表示しているわけでないのでリダイレクトしないと処理が終わっても処理が固まった感じになってしまうからです。

処理としては正常に終了してるけど、↓のように何も起こってない…みたいな感じになる。
正常に登録はできているのでリロードするとちゃんと表示されることになる。

todo1.gif

get '/todos', to: 'todos#index', as: 'todos'
as: 'todos'と書くと/todosというURLにtodosという名前を付けて使えるようにしています。

この名前に、ルーティングの名前(todosなど) + _pathtodos_path
のようにするといちいちURLを書く必要がなくなります。

注意点として、Formで入力した情報を新規登録や更新する際はセキュリティ対策としてStrong Parametersを使わないとエラーでますので追加します。

Strong Parameters

セキュリティ対策で、入れないとエラーになります。
難しいこと省いて、フォームから受け取ったデータを渡すと、許可したデータだけ返却されるくらい覚えておけばとりあえずは良いと思います。

そして追加したのがtodo_paramsメソッド。また、privateを使うと、todo_paramsメソッドが外部から使えないようにできるのでセキュリティを高まります。
難しいことはいいので、privateの下にStrong Parametersいれるんだなくらいで今はいいです。

app/controllers/todos_controller.rb
class TodosController < ApplicationController
  def index
    @todos = Todo.all
    @todo = Todo.new # フォーム作るために必要
  end
  
  def create
    @todo = Todo.create(todo_params)
    redirect_to todos_path
  end
  
  private
    def todo_params
      params.require(:todo).permit(:title)
    end
end

9. ビューファイルはいるか?

データベースでのデータの追加処理では使いません。
ただし、送信するためのテキスト入力フォーム送信ボタンindex.html.erbに登録したいと思います。

テキスト入力フォームと送信ボタン

index.html.erb
<h1>ToDo一覧</h1>
<table>
  <thead>
    <tr>
      <th>タスク</th>
    </tr>
  </thead>
  <tbody>
    <% @todos.each do |todo| %>
      <tr>
        <td><%= todo.title %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<h2>ToDo追加</h2>
<%= form_with model: @todo do |f| %>
  <div><%= f.label :title %></div>
  <div><%= f.text_field :title %></div>
  <div><%= f.submit %></div>
<% end %>

フォームを作るためにform_withを使っています。
また、使うmodelを指定する必要があるので@todoを指定してます。
コントローラーのindexアクションで追加します。

f.labelのようにすると「Title」というラベル(文字表示)され、
f.text_fieldは文字を入力するフォームを作ることができる。
f.submitは送信ボタン。

todos_controllerindexにフォームを作るための部品を用意するため@todo = Todo.newを追加

app/controllers/todos_controller.rb
class TodosController < ApplicationController
  def index
    @todos = Todo.all
    @todo = Todo.new # フォーム作るために必要
  end
end

image.png

登録機能完成

データベースに登録する機能が完成しました!

todo2.gif

今度は更新と削除

次は更新機能を実装します。

必要機能

  • 情報更新ページ(今回はeditアクションとedit.html.erbにて)
    • ルーティングを追加する
    • editアクションで編集する情報を表示する todos_controller.rb
    • ビューで見た目を作る edit.html.erb
  • 情報更新処理
    • updateアクションで情報の更新処理 todos_controller.rb

更新情報編集ページ用ルーティング追加

routes.rb
Rails.application.routes.draw do
  get '/todos', to: 'todos#index', as: 'todos'
  post '/todos', to: 'todos#create'
  
  get '/todos/:id/edit', to: 'todos#edit', as: 'edit_todo'
end

get '/todos/:id/edit', to: 'todos#edit', as: 'edit_todo'を追加しました。

todos_controller.rbeditアクションを使用します。ルーティングの名前はedit_todoとでもしておきます。

更新情報編集ページ用コントローラー

editアクションは、情報編集画面(/todos/:id/edit)にアクセスすると処理されるアクションで、ビューで見た目を作るときに必要なデータの取得します。

editアクションを情報を追加します。

todos_controller.rb
  def edit
    @todo = Todo.find(params[:id])
  end
  • @todoはインスタンス変数。@とつけることでビューでもデータを使えるようになります。
  • findはデータベースのidカラムからデータを探し出すときに使います。
    • idはtodoを追加したときに自動的に採番されていきます。データベースの中身は下の表みたいになる。
  • 編集画面は(/todos/:id/edit)のようにルーティングで設定した。
    • 例えばURLがhttp://sample.com/todos/1/editだとするとparams[:id]1
    • http://sample.com/todos/3/editだとするとparams[:id]3
    • http://sample.com/todos/1/editだとすると@todo = Todo.find(1)と書いているのと同じになる

試しに登録したデータベースの内容が↓になります。

id title created_at updated_at
1 テスト 2019-10-18 04:05:36.776003 2019-10-18 04:05:36.776003
2 買い物 2019-10-18 04:05:43.090180 2019-10-18 04:05:43.090180
3 勉強 2019-10-18 04:05:51.098753 2019-10-18 04:05:51.098753

@todo = Todo.find(params[:id])id1だったら取得するデータをbyebugというデバッグツールで確認してみると
#<Todo id: 1, title: "テスト", created_at: "2019-10-18 04:05:36", updated_at: "2019-10-18 04:05:36">

というデータが取れます。

ここで注意点

教えていて勘違いしている方が多かったので補足。

今回titleカラムのデータを取得したいわけです。
@todo = Todo.find(params[:id])をするとtitleのデータだけを取得しているのではありません。意図的にデータを絞り込まなければ、今回の場合
id, title, created_at, updated_at 1レコード(1行)分のデータ取得されます。

@todo = Todo.find(params[:id]) idは1とすると

正:
#<Todo id: 1, title: "テスト", created_at: "2019-10-18 04:05:36", updated_at: "2019-10-18 04:05:36">

誤:
#<Todo title: "テスト">

自分が必要だと思っているデータだけ取得できてると勘違いしている??ように思えましたので補足。

こらへんはSQLを理解していないから起こることなのかとも思います。
気を付けましょう。

更新処理アクション追加

ルーティングとコントローラーに追記していきます。
resources使うともう少しシンプルに書けますが、今回は原理を知るためしっかりと書いていきます。

routes.rb
Rails.application.routes.draw do
  get '/todos', to: 'todos#index', as: 'todos'
  post '/todos', to: 'todos#create'
  
  get '/todos/:id/edit', to: 'todos#edit', as: 'edit_todo'
  patch '/todos/:id', to: 'todos#update' # 更新処理用ルーティング追加
end
app/controllers/todos_controller.rb
  def update
    @todo = Todo.find(params[:id])
    @todo.update(todo_params)
    redirect_to todos_path
  end
```

resouses使えるならこんな感じでシンプルに書ける

```.erb
<-- resousesを使えるときなら -->
<h1>ToDo編集</h1>
<%= form_with model: @todo do |f| %>
  <div><%= f.label :title %></div>
  <div><%= f.text_field :title %></div>
  <div><%= f.submit %></div>
<% end %>
<%= link_to '戻る', todos_path %>
```

今回原理をよく理解するためにこのようにURLとメソッドもちゃんと書きました。
`#{ }`は文字列として変数を埋め込めるRubyの記法です。
変更するToDoデータの`id`を指定すれば同じことを行えます。

```.erb:edit.html.erb
<h1>ToDo編集</h1>
<%= form_with(model: @todo, url: "/todos/#{@todo.id}", method: :patch) do |f| %>
  <div><%= f.label :title %></div>
  <div><%= f.text_field :title %></div>
  <div><%= f.submit %></div>
<% end %>
<%= link_to '戻る', todos_path %>
```


### 編集ページへのリンクを作る

変更箇所付近だけ抜粋。`link_to`と`~~~~_path`を使ってやるとこんな風に簡単に作れる。


```.erb:index.html.erb
<% @todos.each do |todo| %>
  <tr>
    <td><%= todo.title %></td>
    <td><%= link_to '編集', edit_todo_path(todo) %></td>
  </tr>
<% end %>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/4aea333f-90ef-f433-083b-82ea3461eb10.png)


```.erb
<td><%= link_to '編集', edit_todo_path(todo.id) %></td>
```



ついでに、ちゃんと省略して書かないとこういう書き方になる。`todo` → `todo.id`
Railsがいい感じで判断してくれるのでこのようになりますが、原理は覚えておくほうがいいと思います。
htmlだと↓のようなものが作られる
`<a href="/todos/1/edit">編集</a>`

## 削除機能実装
削除機能はわかってしまえば他と大して変わらないので楽です。
1. ルーティング追加
2. コントローラーに`destroy`アクション追加
3. ビューに削除ボタンのリンク追加



```routes.rb
Rails.application.routes.draw do
  get '/todos', to: 'todos#index', as: 'todos'
  post '/todos', to: 'todos#create'
  
  get '/todos/:id/edit', to: 'todos#edit', as: 'edit_todo'
  patch '/todos/:id', to: 'todos#update'
  delete '/todos/:id', to: 'todos#destroy'
end
```


- `@todo = Todo.find(params[:id])`で削除する項目(idで指定された項目)を@todoに入れる
- destroyメソッドで削除する
- 元のページに戻る

```app/controllers/todos_controller.rb
  def destroy
    @todo = Todo.find(params[:id])
    @todo.destroy
    redirect_to todos_path
  end
```

`<td><%= link_to '削除', "/todos/#{todo.id}", method: :delete %></td>`を追加。
`method: :delete`でメソッドを指定。(指定しないとデフォルトのGETになります。)
ルーティングでdeleteに設定したURL`delete '/todos/:id', to: 'todos#destroy'`なので対応させるために`"/todos/#{todo.id}"`このように書きました。


```.erb:index.html.erb
<% @todos.each do |todo| %>
  <tr>
    <td><%= todo.title %></td>
    <td><%= link_to '編集', edit_todo_path(todo) %></td>
    <td><%= link_to '削除', "/todos/#{todo.id}", method: :delete %></td>
  </tr>
<% end %>
```
削除機能が完成しました。

![todo3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/a04c517e-eeaa-69c5-6b78-b64ae5a41d04.gif)

見た目は最悪ですが、ToDo登録・更新・削除機能が完成しました。
7
9
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
7
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?