Rails: 建立多對多關係(M:N)
前陣子寫了兩篇關於建立好友名單的文章:Rails: 建立好友名單、Rails: 建立好友名單(續)加上好友描述,文中有提到,除非有特殊需求,否則建議建立多對多關係請盡量使用 has_and_belongs_to_many(habtm) 或 has_many :through 的方式來建立。
我個人比較常用到的是 has_many :through,也就是本篇的主題。
範例說明
本範例將建立物品清單管理,每個使用者擁有多個物品,例如 A 擁有電腦、手機、相機;使用者設定物品清單的時候可以建立描述,例如紀錄購入時間、價格或是其他文字描述等等。
資料庫規劃
首先建立兩個 Model 分別為 User、Item,分別對應到 Users 以及 Items 資料表,另外建立記錄兩者 relationship 的 Model 及 Table,使用 migration 來建立內容如下:
# db/migrate/001_create_users.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :name, :string, :null => false
t.column :created_at, :datetime
end
end
def self.down
drop_table :users
end
end
# db/migrate/002_create_items.rb
class CreateItems < ActiveRecord::Migration
def self.up
create_table :items do |t|
t.column :name, :string, :null => false
t.column :description, :text
t.column :created_at, :datetime
end
end
def self.down
drop_table :items
end
end
# db/migrate/003_create_ownerships.rb
class CreateOwnerships < ActiveRecord::Migration
def self.up
create_table :ownerships do |t|
t.column :user_id, :integer, :null => false
t.column :item_id, :integer, :null => false
t.column :description, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :ownerships
end
end
Model 的程式如下,簡單來說就是透過第三個 Model 來記錄兩個 Model 之間的關係:
# app/models/item.rb
class Item < ActiveRecord::Base
has_many :ownerships
has_many :users, :through => :ownerships
end
# app/models/user.rb
class User < ActiveRecord::Base
has_many :ownerships
has_many :items, :through => :ownerships
end
# app/models/ownership.rb
class Ownership < ActiveRecord::Base
belongs_to :item
belongs_to :user
end
如此一來,就可以用下列指令來增加 User, Item,並且可以查詢到某 User 所有的 items,或是 擁有某 item 的 users。
peter = User.create(:name => "Peter")
deduce = User.create(:name => "Deduce")
phone = Item.create(:name => "phone")
camera = Item.create(:name => "camera")
peter.items << phone
peter.items.count
phone.users.count