Rails 多對多關聯設定與存取
使用情境: 每篇貼文 (post)可用設定其屬於多個分類 (category),而每個分類 (category)下有所屬的貼文(post)。
Model 設定
上述情境下的 post 與 category 為多對多關係,需要建立 join table,ERD 如下:
- 使用
has_and_belongs_to_many
設定關聯
class Post < ApplicationRecord
has_and_belongs_to_many :categories
endclass 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_boxes
、collection_select
或 select
來達成,不過要注意的是 Bootstrap 4 目前並不支援 collection_select
及 select
, 所以推薦使用 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。
參考資料:
- How to Save Multiple Checkbox Values to a Database in Rails:主要參考來源
- CreateJoinTable:直接產生寫好的 migration 的指令
- create_join_table:在 migration 檔案內用來建立 join table 的指令
- APIdoc -collection_check_boxes
- Rails 實戰聖經-多對多 Resources
- Strong parameter 參數與 Array 之:老師我可以換位置嗎?