2010-10-26 92 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 

这样,你现在可以在调用问题的选择