RailsでToDoサービスを作ろう(第3回)〜ユーザを紐付ける 〜

こんにちは、にゅ〜ぶるです。
こんにちは〜、ぶるこだよ〜💕


さて、第3回目ですね!前回は、こちら
今日は、「ユーザを紐付ける」について、進めていきたいと思います!

自分だけのToDoリストであれば、不要かもしれませんが、多くの方に利用して貰うには、「ユーザ」は必須になりますよね。
それでは、やっていきましょ〜🎶

コマンドは、こんな感じ
1 |
rails generate scaffold User name:string |
名前 … nameというカラム名の文字列型(string)

その他、追加したいカラムは自由に追加してみてください。
年齢 … ageというカラム名の整数型(integer) など

テーブルを作成するmigrateファイルも作成されますので、
migrateコマンドも忘れずにね!
1 |
rails db:migrate |
わ〜お、忘れるところだった💕


Userの画面を作ったので、「rails s」で起動した後、
「http://127.0.0.1:3000/users」にアクセスしてみてね!


そして、自分のユーザを登録してみましょう。
作成されたCRUDの画面を使って登録しても良いのですが、
今回は、少し脱線しますが、Seedを使ってみましょう。

でも今回使うSeedは、標準で使えるSeedではなく、「Seed Fu」をオススメします。
実行するたびにデータが新規登録されるSeedに比べ、更新が楽になりますので、「Seed Fu」を使っています。

では早速、使ってみましょう。
まずは、Gemfileに以下を追加します。
1 |
gem
'seed-fu',
'~> 2.3' |
Rails 3.1, 3.2, 4.0, 4.1, 4.2, 5.0では、2.3以上である必要があるため、この指定をしています。
参考 https://github.com/mbleigh/seed-fu

そして、Gemfileを修正したら「bundle install」ですね。
1 |
bundle install |

そして、Seedファイルを置く場所を作成します。
1 2 3 |
mkdir db/fixtures mkdir db/fixtures/development mkdir db/fixtures/production |
このディレクトリは固定ですか?


そうだね、変更する事も可能だけど、
基本的には、db/fixtures配下にseedファイルを作成したら良いと思います。
そして、development/productionディレクトリ配下にseedファイルを作成すると、その環境下でのみ実行されるseedファイルになります。

今回は、ユーザ情報を作成しますので、開発環境(development)だけに作成してみましょう。
※seedファイルのファイル名は自由につけても構いませんが、アルファベット順に実行される事に注意してください。
1 2 3 4 5 6 |
User.seed do
|s| s.id
=
1
s.name
=
"にゅ〜ぶる" end User.seed do
|s| s.id
=
2
s.name
=
"ぶるこ" end |

では、実行してみましょう!
1 |
rake db:seed_fu |

こんな感じのログが出力されて、usersテーブルに登録されているのが確認出来るかと思います。
1 2 3 |
==
Seed from
/Users/newburu/project/rails/todo/db/fixtures/development/user.rb
-
User
{:id=>1,
:name=>"にゅ〜ぶる"}
-
User
{:id=>2,
:name=>"ぶるこ"} |

データに変更が発生した際は、このファイルを修正して、再度コマンドを実行するだけでOKです。是非、試してみて下さい。
おぉぉお!!すごぉ〜い💕
データを消したり、DBを作り直したりしなくても変更できた!!


その他、Seed-Fuには色々な書き方がありますが、必要あれば調べてみて下さいね。
では、これで初期データも出来たので、次に進みましょうか!
は〜い💕


次は、作成したTaskとUserを紐づけることをしてみましょう。
紐づける事によって、「にゅ〜ぶるのTask」と「ぶるこのTask」といったように分けることが出来るようになります。

まずは、紐づいた情報を管理するために、テーブルを修正します。
修正する際は、generate機能のmigrationを使います。
1 |
rails generate migration AddUserToTask user:references |

中身はこんなファイルが作成されたと思います。
1 2 3 4 5 |
class
AddUserToTask
<
ActiveRecord::Migration[6.0] def change add_reference
:tasks,
:user,
null:
false,
foreign_key:
true end end |

rails generate migration の後に、「AddUserToTask」としたことで、自動でTaskに追加するのだと判断されて、「user:references」を指定したことで、Userへの参照用のカラムだと判断され、Migrationファイルが自動生成されました。

では、rails db:migrateを実行して、テーブルがどう変わったか確認してみましょう。
1 |
rails db:migrate |
うわ〜ん😭
エラーが出ちゃったよおぉぉ😭

1 |
Mysql2::Error:
Cannot add or
update
a
child
row:
a
foreign key constraint fails
(`todo_development`.`#sql-16e75_a`, CONSTRAINT `fk_rails_4d2a9e4d7e` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)) |

あっ!
前回、Taskを作成した際に、何かデータを登録してますね?
そのデータに対して、ユーザが紐づいていない状態なのに、紐付けキー(必須になる)を作成してしまった為、MySQLのエラーが出ちゃいましたね…💦
※エラーが出なかった方は、少しお待ちください。

では、DBをリセットしましょう。(登録した中身は消えちゃいますが…)
1 |
rails db:migrate:reset |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Dropped database
'todo_development' Dropped database
'todo_test' Created database
'todo_development' Created database
'todo_test' ==
20200429055957
CreateTasks:
migrating
====================================== --
create_table(:tasks) ->
0.0302s ==
20200429055957
CreateTasks:
migrated
(0.0303s)
============================= ==
20200509005333
CreateUsers:
migrating
====================================== --
create_table(:users) ->
0.0268s ==
20200509005333
CreateUsers:
migrated
(0.0268s)
============================= ==
20200509043009
AddUserToTask:
migrating
==================================== --
add_reference(:tasks,
:user,
{:null=>false,
:foreign_key=>true}) ->
0.1002s ==
20200509043009
AddUserToTask:
migrated
(0.1003s)
=========================== |

はい。すっきり笑
よかったぁ〜💕


初期データも消えてしまったので、「rake db:seed_fu」も再度実行しておいて下さいね。

これでDB上ではTaskとUserが紐づいたのですが、Railsにも紐づいている事を教えてあげなければいけません。その時に使うのが、Modelに「has_one/many」「belongs_to」です。

まずは、「has_one/many」を設定しましょう。
has_XXXとなっている事から分かる通り、親と子の関係で、親の方に設定します。
1 2 3 |
class
User
<
ApplicationRecord has_many
:tasks end |

そして、子の方に、「belongs_to」を設定します。
1 2 3 |
class
Task
<
ApplicationRecord belongs_to
:user end |

こう設定する事で、
User.tasksで、そのUserのTask一覧が取得でき、
Task.userで、そのTaskのUserが取得出来ます。
では、実際に試してみましょう。

ちょっと試したいなという時に便利なのが、「rails console」です。
コンソール上からRailsのサービスにアクセスする事が出来ます。
1 2 |
rails console ※略して、「rails
c」でも可 |

Rubyをやった事のある方ならご存知、irbのコンソールが起動します。
このコンソール上で、自由にプログラムを実行させる事が出来るのです。

こんな感じにテストデータを作成してみましょう。
1 2 |
user
=
User.first user.tasks.create(title:
"テスト",
content:
"テストデータ") |

この状態で、次のように実行してみて下さい。なんと返ってくるでしょう?
1 2 |
task
=
Task.first task.user |

こんな結果が返ってきますね。
1 |
=>
#<User id: 1, name: "にゅ〜ぶる", created_at: "2020-05-09 04:53:02", updated_at: "2020-05-09 04:53:02"> |

ほら、TaskとUserが紐づいた!
やったぁ〜💕


これで完成!!!!
と言いたいところですが、画面も直さないといけないですね。

では、画面のどこを直す必要があるでしょうか?
えっと〜
新規登録(C)はいるでしょ〜💕
あとはぁ〜変更(U)に参照(R)に
削除(D)も?


削除は画面がないので、修正する必要はないんだけど、
親を削除したら子も一緒に削除はしておきたいよね。それを最後にやろっか。

では、新規登録の画面は、
「app/views/tasks/new.html.erb」になるんだけど、中身はこんな感じ
1 2 3 4 5 |
<h1>New
Task</h1> <%=
render
'form',
task:
@task
%> <%=
link_to
'Back',
tasks_path
%> |

renderを使って、別ファイルになっているんだ。
そのファイルが「app/views/tasks/_form.html.erb」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<%=
form_with(model:
task,
local:
true)
do
|form|
%> <%
if
task.errors.any?
%> <div id="error_explanation"> <h2><%=
pluralize(task.errors.count,
"error")
%>
prohibited this
task from being saved:</h2> <ul> <%
task.errors.full_messages.each
do
|message|
%> <li><%=
message
%></li> <%
end
%> </ul> </div> <%
end
%> <div class="field"> <%=
form.label
:title
%> <%=
form.text_field
:title
%> </div> <div class="field"> <%=
form.label
:content
%> <%=
form.text_area
:content
%> </div> <div class="field"> <%=
form.label
:deadline
%> <%=
form.date_select
:deadline
%> </div> <div class="actions"> <%=
form.submit
%> </div> <%
end
%> |

これに、ユーザを追加してみましょう。
ユーザはリスト表示したいので、「form_withのselect」を使います。
好きなところに、以下を追加してみて下さい。
1 2 3 4 |
<div class="field"> <%=
form.label
:user_id
%> <%=
form.select
:user_id,
User.all.map
{
|user|
[user.name,
user.id]
}
%> </div> |

おぉ〜💕
出た出た〜登録しよ〜💕


が〜ん❗️


Userに値が入ってない!!って怒られたね。
原因は、ここ。
1 2 3 4 |
app/controllers/tasks_controller.rb def task_params params.require(:task).permit(:title,
:content,
:deadline) end |

Strong Parametersっていうんだけど、
要は、受け取る可能なパラメータリストを指定して、それ以外の不正なパラメータを受け取らないようにしている感じだね。

ここに、userを許可してあげないといけないんだ。
書き方はこう。
1 2 3 |
def task_params params.require(:task).permit(:title,
:content,
:deadline,
:user_id) end |

これで登録できるようになるよ。
わぁ〜💕
登録できたぁぁあ💕

次は、変更画面だね💕


そうだね。
でも実は、「app/views/tasks/_form.html.erb」が変更画面でも使われているので既に修正出来てるんだ。
なんやて工藤!!!


だから、次は、参照画面を修正するよ。
参照画面のViewは、「app/views/tasks/show.html.erb」だね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<p
id="notice"><%=
notice
%></p> <p> <strong>Title:</strong> <%=
@task.title
%> </p> <p> <strong>Content:</strong> <%=
@task.content
%> </p> <p> <strong>Deadline:</strong> <%=
@task.deadline
%> </p> <%=
link_to
'Edit',
edit_task_path(@task)
%>
| <%=
link_to
'Back',
tasks_path
%> |

これは単純に、@taskというインスタンス変数にTaskの情報が入っているから、
そのまま表示するだけだね。
次のコードを追加してみてね。
1 2 3 4 |
<p> <strong>User:</strong> <%=
@task.user
%> </p> |

変な数字みたいなのが…


おっと、それは、クラスのオブジェクトIDなんだけど、まぁ気にしなくても大丈夫です。
こう置き換えてみてね。
1 2 3 4 |
<p> <strong>User:</strong> <%=
@task.user.name
%> </p> |
おぉ〜ユーザの名前が出たぁ💕


うん。これで完成だね!
じゃあ、応用の課題として、一覧画面の「app/views/tasks/index.html.erb」にも修正してみて下さいね。
※回答はあえて載せません。合ってるかどうかの確認が必要であれば、コメントなりでご連絡くださいね。
わぁ〜い、かんせー💕


じゃ、ここからはおまけ?
Userを削除した際に、そのUserに関するTaskも一緒に削除される機能を追加するよ。
(*´∀`*)ワクワク
でも、難しそう…


そう思うかもだけど、実は簡単なんだ。
Modelの紐付け設定に、このオプション(dependent: :destroy)を追加するだけなんだ。
1 2 3 |
class
User
<
ApplicationRecord has_many
:tasks,
dependent:
:destroy end |
なんやて工藤!!!!


では、動作確認してみようか。
画面で動かしてみるのでも良いし、「rails console」を使うのでも良いよ。
「rails console」の場合は、こんな感じかな。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# テスト用Taskを登録 user
=
User.first user.tasks.create(title:
"test1",
content:
"テスト1です") user.tasks.create(title:
"test2",
content:
"テスト2です") # テスト用Taskが登録されたか確認 Task.all # テスト用Userを削除 ※ここで同時にTaskが削除される user.destroy # 削除されたか確認 Task.all |
ほんとだぁ〜すご〜い💕


今回はここまでだよ。お疲れ様でした!
次回は、「Punditで認可の権限管理する」お楽しみにっ!!
最後まで読んでくれてありがとうございました!
現在、Railsのチュートリアル的な感じで、
「Todoサービスを作る!」をテーマにお送りしております。
アジェンダは、こちら
質問等ありましたら、コメントなりTwitterなりで頂ければ対応させて頂きますので、遠慮なく利用くださいね。
ディスカッション
コメント一覧
まだ、コメントがありません