Rails 多對多關聯設定與存取

An-Chieh Kuo (郭安傑/關節)
5 min readJun 19, 2018

--

使用情境: 每篇貼文 (post)可用設定其屬於多個分類 (category),而每個分類 (category)下有所屬的貼文(post)。

Model 設定

上述情境下的 post 與 category 為多對多關係,需要建立 join table,ERD 如下:

  1. 使用has_and_belongs_to_many設定關聯
class Post < ApplicationRecord
has_and_belongs_to_many :categories
end
class Category < ApplicationRecord
has_and_belongs_to_many :posts
end

2. 使用 CreateJoinTable 指令建立 join table 的 migration, 並直接 migrate 即可。

$ rails g migration CreateJoinTableCategoryPost category post
$ rails db:migrate

CreateJoinTable 指令會自動建立 migration 如下:

class CreateJoinTableCategoryPost < ActiveRecord::Migration[5.1]
def change
create_join_table :categories, :posts do |t|
# t.index [:category_id, :post_id]
# t.index [:post_id, :category_id]
end
end
end

Controller 設定

需要傳入 Post 參數的 Controller ( 如 Post Controller )要設定 Strong Parameters。

def post_params
params.require(:post).permit(#other_stuff, category_ids:[])
end

如果不想把 category_ids:[] 放最後面,許要用大括號 {} 包起來,改成:

def post_params
params.require(:post).permit( {category_ids:[]}, #other_stuff)
end

(這段感謝王綠島指正關於 category_ids:[] 順序造成的問題,詳情可見回文或是參考資料6)

View 設定

可選擇使用 collection_check_boxescollection_selectselect 來達成,不過要注意的是 Bootstrap 4 目前並不支援 collection_selectselect , 所以推薦使用 collection_check_boxes

A. collection_check_boxes

<%= form_for @post do |f| %>
<%= f.collection_check_boxes :category_ids, Category.all, :id, :name %>
<% end %>

B. collection_select

<%= form_for @post do |f| %>
<%= f.collection_select :category_ids, Category.all, :id, :name, {:prompt => "Please Select a Sector"}, {:multiple => true} %>
<% end %>

C. select

<%= form_for @post do |f| %>
<%= f.select(:category_ids, Category.all.collect {|c| [ c.name, c.id ] }, { :prompt => "Please select"}, { multiple: true }) %>
<% end %>

MVC 三邊都設好後就能使用了

其他設定

Rake 假資料

如果需要建立 post 假資料,可先建立數筆 category 資料後,利用下面語法設定 post 所屬 category。

Post.create(
category_ids: (1...10).to_a.shuffle.take(rand(1..5)),
# 其他屬性 ...
)

上面語法表示從預先建立好的10筆 category 中隨機且不重複的抓出 1~5 個與 post 作關聯。

Postman API 測試

如有需要用 Postman 對 API 作傳值測試( 例如使用 POST 、 PUT),可在 Body 分頁下的 key 欄位中輸入 category_ids[] ,value 欄位中輸入單一值,一個 post 有幾個關聯的 category 就輸入幾列, 例如下面是新增一筆 post , 其關聯 category 的 id 為 4 與 9。

新增一筆 post,其關聯 category 的 id 為 4 與 9

參考資料:

  1. How to Save Multiple Checkbox Values to a Database in Rails:主要參考來源
  2. CreateJoinTable:直接產生寫好的 migration 的指令
  3. create_join_table:在 migration 檔案內用來建立 join table 的指令
  4. APIdoc -collection_check_boxes
  5. Rails 實戰聖經-多對多 Resources
  6. Strong parameter 參數與 Array 之:老師我可以換位置嗎?

--

--