精華區beta Ruby 關於我們 聯絡資訊
: ※ 發信站: 批踢踢實業坊(ptt.cc) : ◆ From: 61.230.103.247 : → ihower:改用has_many :through吧~ 11/01 22:31 : → ihower:http://ihower.idv.tw/blog/archives/1437 11/01 22:32 : → contagious:在 << 的問題還沒正式解決之前..覺得還是先用 HABTM 好 11/02 01:09 : → contagious:http://rubyurl.com/qpg 解決進 edge 了..等等等... 11/02 01:11 感謝您的指教,我昨天也發現到這樣會有問題了 以下是新的文章 http://lightyror.blogspot.com/2006/11/nm-join-table.html 原本的設計 N:M Join Table 一般來說,通常只有兩個 Column ,兩個都是另外兩個 table 的 Foreign Key。舉例來說,假設今天有兩個 Table ,一個是 people ,另外一個是 firend_relations。人跟人之間的朋友關係由 friend_relations 來描述,所以 friend_relations 裡面的欄位應該有 person_id friend_person_id 根據 Agile Web Development with Rails這本書,裡面有一段話 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 這個欄位的話,當我們使用 Person.find(1).friends.each do |friend| puts friend.id end 這裡的 i.id 不是朋友在 people 這個 table 裡面的 id ,反而是 Join Table friend_relations 的 id 。也就是說,Join Table 裡面除了 Foreign Key 以外的欄位,都會覆蓋掉所有關聯性 Table 的欄位。這在 Coding 時,很容易造成混淆,必須盡量避免。 好處? 但是有時候這個特性很好用,假設有一需求。我想知道我跟你之間的朋友關係是啥時建立的?我們如果將這個時間 created_at 寫在 Join_Table frined_relations 裡面,那麼 Person.find(1).friends.each do |friend| puts frined.created_at end 這裡的 i.created_at 就是 freind_relations 裡面的 created_at (朋友關係建立的時間),而非 people 裡面的 created_at (person 資料建立的時間),因為已經被覆蓋掉了。的確,當你使用到 person.friends 的時候,通常你在使用 朋友關係 的資料,而非 這個人的基本資料 ,所以他被覆蓋過去也是很合理的。 我們還可以在 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) b = Person.find(2) a.friends << b 這個時候,friend_relations 裡面的 created_at 的確有新的值輸入,但可惜不是創造關係的時候的時間,而是 b 的 created_at 時間。如果我在 friend_relations 加入一個 Person 原本就有的 column,像是 name 好了 。我發現到執行上面的程式他會直接將 b 的 name 欄位直接寫進去,這真是太有趣了。 根據 Rails 的 Many-to-Many 討論 這篇的講法 主要來說 has_and_belongs_to_many 比較簡單,但是那個 join table 不適合再加欄位值,比如說該關聯建立的時間之類的,雖然 push_with_attributes 這個方法可以塞資料進去,但是卻是很亂的糟方式,已經不被推薦使用。 而 has_many :through 功夫多一些,結構更完整,用新增一個 join model 的方式來建立關聯資料。自己有一個 model class,因此使用上也比較豐富。就像一般 model 一樣,先 new 一個出來,assign 好那兩個關聯物件,再填好其它資料,最後 save 即可。 還有 Many-to-many Dance-off! 讓我新發現到 has_many :through 這個方式。我正在仔細思考重構的方式。不過在 Project 重構之前,我先使用 push_with_attribute 進行簡單的 Bug Fix 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 還能用,其他的就有問題了。 -- lighty RoR 是一個介紹 lighttpd , SQLite , Ruby and Rails 的 Blog http://lightyror.blogspot.com/ -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 61.218.90.242