LoginSignup
31
32

More than 3 years have passed since last update.

[Rails]多対多のモデルの実装、コントローラー、ビューの書き方

Posted at

はじめに

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
migrationファイル
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ガイドの多対多のリレーションの項を参照しました。

models/category.rb
class Category < ApplicationRecord
  has_many :work_categories #1
  has_many :works, through: :work_categories #2
end

#1#2はこの順番で書かないとエラーが出ます。以下、同じです。

models/work.rb
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
models/work_category.rb
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の編集

controllers/works_controller.rb
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をチェックボックスで表示し、チェックを入れるとカテゴリーの値が設定・編集できるようにします。

▼作りたいものはこんな感じ

Image from Gyazo

▼書いたコードはこちら

controllers/works_controller.rb
= collection_check_boxes(:work, :category_ids, Category.all, :id, :name ) do |t|
      =t.label { t.check_box + t.text }

こちらのコードの意味に関しては、以前書いた記事があるので、拙著ですがこちらをご参照ください。

【初心者向け】チェックボックスの書き方あれこれ[Ruby][Rails]

さて、以上をもって多対多の関係を持ったモデル・コントローラー・ビューの実装が一通り完成しました。いざやり始めると細かい気遣いが色々必要でしたので、また他のテーブルとのリレーションを組むときに、参照していきたいです。

最後まで読んでくださり、ありがとうございました。

31
32
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
31
32