為你的 N:M Join Table 加入屬性
原本的設計
N:M Join Table 一般來說,通常只有兩個 Column ,兩個都是另外兩個 table 的 Foreign Key。舉例來說,假設今天有兩個 Table ,一個是 people ,另外一個是 firend_relations。人跟人之間的朋友關係由 friend_relations 來描述,所以 friend_relations 裡面的欄位應該有
好處?
但是有時候這個特性很好用,假設有一需求。我想知道我跟你之間的朋友關係是啥時建立的?我們如果將這個時間 created_at 寫在 Join_Table frined_relations 裡面,那麼
我們還可以在 friend_relations 加入 relation 這個 column ,裡面放 '點頭之交' ,'朋友' ,'好友','密友' 之類的關係,可以作出
Bug? 還是特色?
不過今天發現了一個問題,HABTM 的 Table 在 Ruby on Rails 裡面是 read only ,我們無法寫入 Attribute (因為他沒有相對應的 Model),並且當我們使用 << 傳入 HABTM 關係時,我發現到假設 JOIN Table 有任何 column 跟傳入的 Column 同名,他會直接寫入 JOIN Table 裡面,也就是說假設,friend_relations 跟 people 都有 created_at 這個 column
column,像是 name 好了 。我發現到執行上面的程式他會直接將 b 的 name 欄位直接寫進去,這真是太有趣了。
根據 Rails 的 Many-to-Many 討論 這篇的講法
達成最起碼的 BUG Fix。
結論
這個特性已經 缺點多於優點了 ,不推薦。雖然 push_with_attributes 可以寫入,但是不能修改,所以只能作一次性的寫入,像是 created_at 還能用,其他的就有問題了。
N:M Join Table 一般來說,通常只有兩個 Column ,兩個都是另外兩個 table 的 Foreign Key。舉例來說,假設今天有兩個 Table ,一個是 people ,另外一個是 firend_relations。人跟人之間的朋友關係由 friend_relations 來描述,所以 friend_relations 裡面的欄位應該有
- person_id
- friend_person_id
Active Record automatically includes all columns from the join tables when accessing rows using it. If the join table included a column called id, its id would overwrite the id of the rows in the joined table.如果 join table 裡面,加上 id 這個欄位的話,當我們使用
這裡的 i.id 不是朋友在 people 這個 table 裡面的 id ,反而是 Join Table friend_relations 的 id 。也就是說,Join Table 裡面除了 Foreign Key 以外的欄位,都會覆蓋掉所有關聯性 Table 的欄位。這在 Coding 時,很容易造成混淆,必須盡量避免。Person.find(1).friends.each do |friend|
puts friend.id
end
好處?
但是有時候這個特性很好用,假設有一需求。我想知道我跟你之間的朋友關係是啥時建立的?我們如果將這個時間 created_at 寫在 Join_Table frined_relations 裡面,那麼
這裡的 i.created_at 就是 freind_relations 裡面的 created_at (朋友關係建立的時間),而非 people 裡面的 created_at (person 資料建立的時間),因為已經被覆蓋掉了。的確,當你使用到 person.friends 的時候,通常你在使用 朋友關係 的資料,而非 這個人的基本資料 ,所以他被覆蓋過去也是很合理的。Person.find(1).friends.each do |friend|
puts frined.created_at
end
我們還可以在 friend_relations 加入 relation 這個 column ,裡面放 '點頭之交' ,'朋友' ,'好友','密友' 之類的關係,可以作出
結果就會出現Person.find(1).friends.each do |friend|
puts friend.name+'是'+friend.relation
end
小李是好友之類的結果,可說是相當方便的特性。
小王是點頭之交
小陳是朋友
Bug? 還是特色?
不過今天發現了一個問題,HABTM 的 Table 在 Ruby on Rails 裡面是 read only ,我們無法寫入 Attribute (因為他沒有相對應的 Model),並且當我們使用 << 傳入 HABTM 關係時,我發現到假設 JOIN Table 有任何 column 跟傳入的 Column 同名,他會直接寫入 JOIN Table 裡面,也就是說假設,friend_relations 跟 people 都有 created_at 這個 column
a = Person.find(1)這個時候,friend_relations 裡面的 created_at 的確有新的值輸入,但可惜不是創造關係的時候的時間,而是 b 的 created_at 時間。如果我在 friend_relations 加入一個 Person 原本就有的
b = Person.find(2)
a.friends << b
column,像是 name 好了 。我發現到執行上面的程式他會直接將 b 的 name 欄位直接寫進去,這真是太有趣了。
根據 Rails 的 Many-to-Many 討論 這篇的講法
還有 Many-to-many Dance-off! 讓我新發現到 has_many :through 這個方式。我正在仔細思考重構的方式。不過在 Project 重構之前,我先使用 push_with_attribute 進行簡單的 Bug Fix主要來說 has_and_belongs_to_many 比較簡單,但是那個 join table 不適合再加欄位值,比如說該關聯建立的時間之類的,雖然 push_with_attributes 這個方法可以塞資料進去,但是卻是很亂的糟方式,已經不被推薦使用。
而 has_many :through 功夫多一些,結構更完整,用新增一個 join model 的方式來建立關聯資料。自己有一個 model class,因此使用上也比較豐富。就像一般 model 一樣,先 new 一個出來,assign 好那兩個關聯物件,再填好其它資料,最後 save 即可。
class User < ActiveRecord::Base如此最起碼可以使用
def add_friend user
firendss.push_with_attributes( user , :created_at => Time.now )
end
end
a = Person.find(1)
b = Person.find(2)
a.add_friend(b)
達成最起碼的 BUG Fix。
結論
這個特性已經 缺點多於優點了 ,不推薦。雖然 push_with_attributes 可以寫入,但是不能修改,所以只能作一次性的寫入,像是 created_at 還能用,其他的就有問題了。
沒有留言:
張貼留言