ユーザーとタスクの紐付け、タスクとカテゴリの紐付け
経緯
ユーザーとタスクの紐付け方が分からなかったので、コンソールの辺りをやり直してみる
事前準備
① 適当にアプリを作成
$ rails _5.2.6_ new console_app #(Ruby,Railsのバージョンは任意だったので5系)
② DB作成
$ rails db:create
③ テーブル作成
・usersテーブル name:string age:integer
・tasksテーブル name:string user_id:bigint(references)
・categoriesテーブル name:string
・task_categoriesテーブル task_id:bigint(references) category_id:bigint(references)
モデル作成
$ rails g model {モデル名(単数)} {カラム名:データ型}
データ型: references(参照)
task: references category: references
と書くことで、マイグレーションファイルに外部キー制約がつく。また、task
というカラム名を自動的にtask_id
にしてくれる。Rails6系であれば、NOT NULL制約も付く。
$ rails g model task_category task_id:integer category_id:integer
$ rails g model task_category task:references category:references
外部キー制約とは
外部キーの概要と制約を使うことのメリット・デメリット - Qiita
主キーと外部キーを使った制約で利用した場合、下記の制限が入る。
1. 存在しない値を外部キーとして登録することはできない。
2. 子テーブルの外部キーに値が登録されている親テーブルのレコードは削除できない。
テーブル作成時にrails g model task_category task:references category:references
と記述して、外部キーを作成したことでマイグレーションファイルには以下の記述が追加されている。
foreign_key :true
が外部キー制約
自分は5系でアプリ作成したため、NOT NULL制約は手動で記載。
アソシエーションの設定
app/models/user.rb
とtask.rb
にそれぞれ関係を記述する
ただ、先ほどのreferences
というデータ型のおかげで、task.rb
にはあらかじbelongs_to :user
の記載がある。
(文字的にuserに所属している感が分かりやすいなと・・・)
解釈が合っているか分からないけど、多分ユーザーはたくさんのタスクを持っているみたいな意味でhas_many :tasks
なんだと思う。こちらは手動で記載すること!!
dependent: :destroy
は親モデルを削除すると子モデルも一緒に削除される記述。SNSでユーザーが退会したら、そのユーザーの投稿も全て削除されるという例がわかりやすかった。
[Rails] dependent: :destroy について - Qiita
演習部分
演習部分はネタバレ回避のためPCメモに留めておきます。
tasksとcategories、task_categories(中間テーブル)のアソシエーションを記載
データ型: references
で作成したため、task_category.rb
にはbelong_to
が記載されている。(タスクとカテゴリに所属している)
task_category.rb
task.rb
category.rb
through: :task_categories
とは
タスクとカテゴリは直接紐づいているわけではないので、task_categoriesテーブルを経由してデータを取ってこれる様にthrough: :task_categories
を記載する。
記載がないと?
category.rb
とtask.rb
にそれぞれhas_many :categories, through: :task_categories
と
has_many :tasks, through: :task_categories
の記載がない場合、task.categories
と入力して、直接データを入手することができない。
irb(main):007:0> task = Task.first Task Load (0.1ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" ASC LIMIT ? [["LIMIT", 1]] => #<Task id: 1, name: "Railsの勉強をする", user_id: 1, created_at: "2022-01-08 06:20:29", up... irb(main):008:0> task.categories Traceback (most recent call last): 1: from (irb):8 NoMethodError (undefined method `categories' for #<Task:0x0000000131223e18>) Did you mean? task_categories
↑こんな感じでエラーになってしまう。
(上に書いた通り、タスクとカテゴリは直接紐づいていないからtask_categories
のこと?と聞かれている)
かと言って、下記の様にtask.task_categories
と打ってもIDしか出てこないので、どの情報が入っているのか分かりづらい。
irb(main):027:0> task.task_categories TaskCategory Load (0.1ms) SELECT "task_categories".* FROM "task_categories" WHERE "task_categories"."task_id" = ? LIMIT ? [["task_id", 1], ["LIMIT", 11]] => #<ActiveRecord::Associations::CollectionProxy [#<TaskCategory id: 1, task_id: 1, category_id: 1, created_at: "2022-01-08 07:17:51", updated_at: "2022-01-08 07:17:51">]>
このため、through: :task_categories
を用いる
task.rb
category.rb
task.categoriesと指定しても、データを取ってくることができた!
irb(main):010:0> task = Task.first Task Load (0.1ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" ASC LIMIT ? [["LIMIT", 1]] => #<Task id: 1, name: "Railsの勉強をする", user_id: 1, created_at: "2022-01-08 06:20:29", up...
irb(main):011:0> task.categories Category Load (0.1ms) SELECT "categories".* FROM "categories" INNER JOIN "task_categories" ON "categories"."id" = "task_categories"."category_id" WHERE "task_categories"."task_id" = ? LIMIT ? [["task_id", 1], ["LIMIT", 11]] => #<ActiveRecord::Associations::CollectionProxy [#<Category id: 1, name: "カテゴリ1", created_at: "2022-01-08 06:44:32", updated_at: "2022-01-08 06:44:32">]>
console豆知識
reload!
irb(main):014:0> reload! Reloading... => true