2012-09-23 34 views
0

我有一個Family類,其中包括mother_idfather_id。從家庭模式的角度來看,瞭解哪個父母是母親,哪個父親是重要的,但母親和父親擁有所有相同的屬性(即數據庫列)。所以理想情況下,我希望我的模型文件看起來像這樣:has_one帶有多個可能的外鍵列

class Resident < ActiveRecord::Base 
    has_one :family, :dependent => :nullify, :foreign_key => :father_id 
    has_one :family, :dependent => :nullify, :foreign_key => :mother_id 
    attr_accessible :email, :cell, :first_name, :last_name 
end 

class Family < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Resident', :foreign_key => 'father_id' 
    belongs_to :mother, :class_name => 'Resident', :foreign_key => 'mother_id' 
    attr_accessible :address, :city, :state, :number_of_children 
end 

這是行不通的。 my_family.mothermy_family.father工作,所以Rails似乎很高興與雙belongs_to。但是,my_dad.family == nil,表明第二個has_one重寫第一個。這是合理的,否則,如果resident_id出現在mother_id和father_id列中,會發生什麼情況? (雖然我計劃添加模型級別驗證以確保永不發生,但has_one不會與驗證方法進行交談。)此外,my_dad.family = Family.new是什麼意思? ActiveRecord如何選擇是否將my_dad.id插入Family.mother_idFamily.father_id

this Stackoverflow question,我得到了主意,使用不同的名稱,即has_one線更改爲:

has_one :wife_and_kids, :class_name => 'Family', :dependent => :nullify, :foreign_key => :father_id 
has_one :husband_and_kids, :class_name => 'Family', :dependent => :nullify, :foreign_key => :mother_id 

我的問題是:

1)是否有更好的方式來做到這一點?一個不同的數據庫模式,也許?

2)是數據庫級別的驗證可以補充模型級驗證,以確保my_dad.id不能同時在mother_idfather_id欄顯示?

3)你能想到比husband_and_kids/wife_and_kids更好的名字嗎? (誠​​然不是一個編程的問題...)

編輯: 它發生在我加入一個家庭的getter:

def family 
    @family ||= self.wife_and_kids || self.husband_and_kids 
end 
after_save :reset_family 
def reset_family 
    @family = nil 
end 

這使得語法更清潔(因爲我真的不是一個風扇的[husband|wife]_and_kids),沒有造成任何歧義,因爲沒有設置。

回答

0

你面臨的主要問題是,你有一個「條件」外鍵,這意味着用於解析外鍵:家庭居民的取決於居民是否是男性或女性(母親或父親) 。在我看來,處理這個問題的最好方法是使用STI(單表繼承)來區分這兩種情況。

class Resident < ActiveRecord::Base 
    attr_accessible :email, :cell, :first_name, :last_name 
end 

class Mother < Resident 
    has_one :family, :dependent => :nullify, :foreign_key => :mother_id 
end 

class Father < Resident 
    has_one :family, :dependent => :nullify, :foreign_key => :father_id 
end 

你仍然可以使用住戶表,但你需要遷移:string類型的類型字段和存儲值「母親」或「父親」視情況而定。另外,將這些類定義中的每一個放置在models /中它自己的文件中。

編輯:我覺得這也解決了你的第二個和第三個問題提出的各種問題。

EDIT2:

鑑於目前的模式,你需要在你的families表中創建一個check constraint。首先,活動記錄沒有直接的支持,所以你將不得不執行原始的sql來添加約束。從理論上講,每次在「家庭」的「mother_id」欄中添加或更改一個值時,支票必須與「居民」表互相參照,確定「居民」的「類型」欄是「母親。」這將(理論上)添加此約束的SQL是

ALTER TABLE families 
ADD CONSTRAINT must_be_mother CHECK ((SELECT type FROM residents WHERE residents.id = families.mother_id) = 'Mother') 

的問題是,這CHECK包含一個子查詢,而據我所知,在檢查子選擇被許多數據庫不允許的。 (具體細節請見question)。

如果你真的想在這裏實現數據庫級驗證,你可能需要通過將「居民」分成「母親」和「父親」來改變模式。

+0

我不明白爲什麼它會回答我的第二個問題。如果居民有類型'男性',但我嘗試將其ID插入到Family.mother_id中,數據庫將不知道它是無效的。 –

+0

我想它間接地回答第二個問題,因爲它使得明確的驗證是不必要的。創建新記錄時,如果限制自己僅實例化Mother對象或Father對象,則外鍵的含義不會有任何歧義。 Mother對象在family表中有一個外鍵:mother_id,並且這種方式無法與以下內容混淆:father_id – cdesrosiers

+0

但是該驗證在模型中,即在應用程序中。問題#2是關於數據庫級的驗證 - 如果將來有另一個應用程序需要連接到數據庫會怎麼樣 - 我該如何強制未來應用程序的開發人員將無法將父親放入mother_id列的錯誤? 當你運行rails生成model family mother_id:integer:uniq'時,rails會在模式文件中放入: 'add_index「family,[」mother_id「],:name =>」index_families_on_mother_id「,:unique => true' 我想要類似的驗證,說沒有mother_id列可以輸入father_id列 –