はじめに
Railsで多対多のモデルを作り、コントローラー・ビューを実装しました。過去にもやったことがあったので、できるかなーと思ったら、かなり迷ってしまったので、手順をメモしておきます。
データベース設計
今回のテーブルはこんな感じ。作品(works)は複数のカテゴリー(categories)をもち、それぞれのカテゴリーにも複数の作品が属しています。
worksテーブル
column | type |
---|---|
id | integer |
name | string |
description | string |
... | ... |
categoriesテーブル
column | type |
---|---|
id | integer |
name | string |
work_categoriesテーブル(中間テーブル)
column | type |
---|---|
id | integer |
category_id | integer |
work_id | integer |
ところで、現在、特に中間テーブルに命名規則はないようなのですが(※)。2つのテーブル名を連結した名前をつける場合には、__アルファベット順につける__のが慣習のようです。
私は知らずにwork_category
テーブルという名前にしてしまったので、以下の記述でもそのまま書かせていただきます。。。
※ has_and_belongs_to_many
を使っていた頃には命名規則があったみたいです。今は、極力意味のあるテーブル名にするのがルールということです。
migrationファイルの記述
中間テーブルのmigrationファイルを作るときには若干ポイントがありました
$ rails g model work_category
def change
create_table :work_categories do |t|
t.references :work, index: true
t.references :category, index: true, foreign_key: true
t.timestamps
end
end
この記述で、work_idとcategory_idカラムがそれぞれ生成されます。各項にindex: true
オプションをつけて、検索を高速化。foreign_key: true
オプションをつけて、categoryテーブルにないカテゴリーが追加されないようにしています。
ただし、foreign_key: true
をつけると依存関係にある他のテーブルのカラムを自由に編集できなくなるので、今回の練習用のアプリのように、考えながら作る場合は、最後に外部キー制約をつけても良いのかもしれません。。。
▼参考リンク
外部キーの概要と制約を使うことのメリット・デメリット
modelにアソシエーションを追記
work, category, work_categoryのモデルに、それぞれ以下のようにアソシエーションを追記します。
単数形、複数形の気遣いが必要で、Railsガイドの多対多のリレーションの項を参照しました。
class Category < ApplicationRecord
has_many :work_categories #1
has_many :works, through: :work_categories #2
end
#1
と#2
はこの順番で書かないとエラーが出ます。以下、同じです。
class Work < ApplicationRecord
has_many :work_categories, dependent: :destroy
has_many :categories, through: :work_categories
accepts_nested_attributes_for :work_categories, allow_destroy: true
end
class WorkCategory < ApplicationRecord
belongs_to :work
belongs_to :category
end
dependent: :destroy
は、依存関係にあるデータも削除するオプションで、もし、あるworkが削除されたら、それに関連するwork_categoriesテーブルのデータも削除されます。
accepts_nested_attributes_for
は、関連する項目も含めて一気に更新、削除する際の設定項目です。
dependent: :destroy
があればいらないのでは??と思うけれども、カテゴリーの内容が変更されたときに、これがあると便利なのだろうか??ちょっと検証が必要な部分です。(後日追記するかもしれません)
なお、この辺りに関しては、こちらの記事が大変ためになりました。
▼参照した記事
railsで多対多な関係を実装する時のポイント(加筆修正するかも)
controllerのwork_paramsの編集
def work_params
params.require(:work).permit(:name, :description, { category_ids: [] })
end
Workを作成(new)・編集(edit・update)するときに、work_categoriesテーブルにデータを入れられるよう、strong parameterに { category_ids: [] }
でwork_categoriesの配列の入力を許可しています。
viewでカテゴリー一覧をチェックボックスすで表示し、編集できるようにする
works_controllerのnewとeditで、categoryをチェックボックスで表示し、チェックを入れるとカテゴリーの値が設定・編集できるようにします。
▼作りたいものはこんな感じ
▼書いたコードはこちら
= collection_check_boxes(:work, :category_ids, Category.all, :id, :name ) do |t|
=t.label { t.check_box + t.text }
こちらのコードの意味に関しては、以前書いた記事があるので、拙著ですがこちらをご参照ください。
【初心者向け】チェックボックスの書き方あれこれ[Ruby][Rails]
さて、以上をもって多対多の関係を持ったモデル・コントローラー・ビューの実装が一通り完成しました。いざやり始めると細かい気遣いが色々必要でしたので、また他のテーブルとのリレーションを組むときに、参照していきたいです。
最後まで読んでくださり、ありがとうございました。