2010-10-26 122 views
104

鑑於以下關聯,我需要引用QuestionChoice連接從Choice模型。我一直在嘗試使用belongs_to :question, through: :answer來執行此操作。belongs_to通過協會

class User 
    has_many :questions 
    has_many :choices 
end 

class Question 
    belongs_to :user 
    has_many :answers 
    has_one :choice, :through => :answer 
end 

class Answer 
    belongs_to :question 
end 

class Choice 
    belongs_to :user 
    belongs_to :answer 
    belongs_to :question, :through => :answer 

    validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ] 
end 

我越來越

NameError未初始化不斷User::Choice

當我嘗試做current_user.choices

它工作正常,如果我不包括

belongs_to :question, :through => :answer 

但我想使用它,因爲我想能夠做到這一點validates_uniqueness_of

我可能忽略了一些簡單的東西。任何幫助,將不勝感激。

+0

也許是值得改變的接受答案給代表嗎? – 23inhouse 2016-06-07 09:11:52

回答

47

A belongs_to協會不能有:through選項。你最好緩存question_idChoice並添加一個唯一的索引表(特別是因爲validates_uniqueness_of容易出現競爭條件)。

如果您是偏執狂,請將自定義驗證添加到Choice,以確認答案是question_id匹配,但聽起來像最終用戶永遠不應該有機會提交可能會導致此類不匹配的數據。

+0

謝謝斯蒂芬,我真的不想直接與question_id關聯,但我想它是最簡單的方法。 我最初的想法是,既然「答案」屬於「問題」,我總是可以通過「答案」來找到「問題」。但是,你認爲這樣做不容易嗎,或者你認爲那只是一個糟糕的模式? – vinhboy 2010-10-27 15:57:48

+0

如果您想要唯一的約束/驗證,範圍字段必須存在於同一個表中。請記住,有競爭條件。 – stephencelis 2010-10-27 16:12:40

-1

has_many :choices創建名爲choices的關聯,而不是choice。請嘗試使用current_user.choices

有關has_many魔法的信息,請參閱ActiveRecord::Associations文檔。

+1

感謝您的幫助邁克爾,但是,這是我的一個錯字。我已經在做current_user.choices。這個錯誤與我想將belongs_to分配給用戶和問題有關。 – vinhboy 2010-10-27 15:01:17

1

這聽起來像你想要的是一個用戶誰有很多問題。
該問題有很多答案,其中之一是用戶的選擇。

這是你在追求什麼?

我會沿着這些路線模型類似的東西:

class User 
    has_many :questions 
end 

class Question 
    belongs_to :user 
    has_many :answers 
    has_one :choice, :class_name => "Answer" 

    validates_inclusion_of :choice, :in => lambda { answers } 
end 

class Answer 
    belongs_to :question 
end 
4

我的方法是讓而不是添加數據庫列的虛擬屬性。

class Choice 
    belongs_to :user 
    belongs_to :answer 

    # ------- Helpers ------- 
    def question 
    answer.question 
    end 

    # extra sugar 
    def question_id 
    answer.question_id 
    end 
end 

這種方法很簡單,但帶有折衷。它需要Rails從db加載answer,然後question。這可以在稍後通過加載所需的關聯(即c = Choice.first(include: {answer: :question}))進行優化,但是,如果此優化是必需的,那麼stephencelis的答案可能是更好的性能決策。

某些選擇有時間和地點,我認爲這個選擇在原型設計時更好。除非我知道這是一個罕見的用例,否則我不會將它用於生產代碼。

292

您也可以委託:

class Company < ActiveRecord::Base 
    has_many :employees 
    has_many :dogs, :through => :employees 
end 

class Employee < ActiveRescord::Base 
    belongs_to :company 
    has_many :dogs 
end 

class Dog < ActiveRecord::Base 
    belongs_to :employee 

    delegate :company, :to => :employee, :allow_nil => true 
end 
+14

+1,這是最簡單的方法。 (至少我能想到) – Orlando 2012-09-06 18:50:55

+5

+1,http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/ – shweta 2013-04-12 05:24:57

+5

有沒有辦法用JOIN來做到這一點它不使用這麼多的查詢? – Tallboy 2013-10-04 01:44:37

75

只需使用has_one,而不是belongs_to:though,像這樣:

class Choice 
    belongs_to :user 
    belongs_to :answer 
    has_one :question, :through => :answer 
end 

無關,但我很猶豫使用validates_uniqueness_of,而不是使用數據庫中一個適當的唯一約束。當你用紅寶石來做這件事時,你會遇到競爭狀況。

+27

這個解決方案的大警告。每當你保存Choice時,除非設置了'autosave:false',否則它總是會保存問題。 – 2014-08-20 18:45:32

+0

@ChrisNicola你能解釋一下你的意思嗎,我不明白你的意思。 – aks 2018-01-19 12:09:19

+0

我的意思是在哪裏?如果你的意思是一個適當的唯一約束,我的意思是添加一個UNIQUE索引到數據庫中必須唯一的列/字段。 – 2018-01-24 19:49:46

15

你可以簡單地代替使用has_onebelongs_to

has_one :question, :through => :answer 
+10

這個答案看起來非常相似http://stackoverflow.com/a/15649020/38765 – 2015-08-10 06:06:49

+0

同意@AndrewGrimm。這個答案和mrm的答案是一樣的,除了一年半之後。 – jeffdill2 2016-09-27 16:01:46

+1

mmmmmmmmmmrmrmrmrmmr有多可疑 – zeion 2017-03-08 17:58:29

0

所以你不能有你想要的行爲,但你可以做一些事情,感覺就像是。你要能夠做到Choice.first.question

我在過去所做的那樣是這樣

class Choice 
    belongs_to :user 
    belongs_to :answer 
    validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ] 
    ... 
    def question 
    answer.question 
    end 
end 

這樣,你現在可以在調用問題的選擇