2012-05-03 84 views
4

我們的Rails 3應用程序有型號爲PersonMessage。消息可以是特定於個人的(當消息person_id列已設置)或者它們可以是「全局」(當person_id列爲NULL時)。Rails「has_many」與NULL外鍵的關聯

我們希望有使用簡單has_many關係:conditions option這樣:

class Person < ActiveRecord::Base 
    has_many :messages, 
     :conditions => proc { ['(messages.person_id IS NULL) OR ' + 
          '(messages.person_id = ?)'], self.id } 
    # ... 
end 

但現在看來,該has_many類方法執行後編碼「條件」選項爲邏輯「與」條款與Person對象的ID相等的外鍵約束(例如「FROM messages WHERE person_id=123 AND (person_id IS NULL OR person_id=123)」)。看起來沒有辦法允許具有空外鍵的關聯對象屬於這種關聯。

Rails 3/ActiveRecord提供了一種方法來做到這一點,或者我必須破解我自己的關聯方法嗎?

回答

1

像你想的使用狀況下,你不能有一個OR子句ActiveRecord關聯。你可以沒有條件的關聯,然後添加一個方法來包含你想要的全局消息。這樣,在構建相關記錄時,您仍然可以利用該關聯。

# person.rb 
has_many :messages 

def all_messages 
    Message.where('messages.person_id=? OR messages.person_id IS NULL', id) 
end 

這裏是我的Squeel寶石標準插頭,這是非常方便的像這樣的,如果你不希望有SQL位代碼中的更高級的查詢。一探究竟。

+0

不幸的是,這意味着你不能這樣做:Person.limit(2).includes(:all_messages),因爲all_messages不是關聯。 –

+0

@JoeVanDyk是的,你說得對。但是,包含(:all_messages)會執行額外的SQL查詢來執行急切加載。因此,除了語法外,根據我的建議,在性能方面沒有實際差異。 –

+0

執行包含(:all_messages)會導致最多一個額外的查詢。如果你確實Person.limit(100).includes(:all_messages),這會導致兩個查詢。如果您使用上面的#all_messages,那將會是101個查詢來加載所有內容。 –

0

我認爲你是正確的,你可以放棄的has_many,並創建一個範圍與外部連接,這樣的:

class Person < ActiveRecord::Base 
    scope :messages lambda { 
     joins("RIGHT OUTER Join messages on persons.id = messages.person_id") 
     .where('persons.id = ?',self.id) 
    }