通过关联的belongs_to
Posted
技术标签:
【中文标题】通过关联的belongs_to【英文标题】:belongs_to through associations 【发布时间】:2011-04-30 14:19:10 【问题描述】:鉴于以下关联,我需要从Choice
模型中引用Choice
附加的Question
。我一直在尝试使用belongs_to :question, through: :answer
来执行此操作。
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
我可能忽略了一些简单的事情。任何帮助将不胜感激。
【问题讨论】:
也许值得将接受的答案更改为委托人? 【参考方案1】:您也可以委托:
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
【讨论】:
+1,这是最干净的方法。 (至少我能想到) 有没有办法用 JOIN 做到这一点,这样它就不会使用这么多的查询? 我想了解自己。我尝试的一切都触发了 3 次选择。您可以在关联上指定“-> joins :something ” lambda。连接被触发,但随后还是另一个选择。我无法对此进行调整。 @Tallboy 一些关于主键的完美索引选择查询几乎总是优于任何单个 JOIN 查询。联接使数据库工作更加努力。 @affinities23 :这取决于您的特定要求。您可能想要没有任何公司的“不稳定”员工。然而,这里的allow_nil 意味着如果dog.employee
为nil,那么对dog.company
的调用将不会失败并且也只会返回nil。即使你总是希望这些东西是必需的,拥有它也很好,因为你总是可以去 Rails 控制台并输入dog = Dog.new
。那时您的记录不存在,您仍然可以执行dog.company
而不会失败。【参考方案2】:
只需在您的:through
中使用has_one
而不是belongs_to
,如下所示:
class Choice
belongs_to :user
belongs_to :answer
has_one :question, :through => :answer
end
不相关,但我会犹豫是否使用 validates_uniqueness_of 而不是在您的数据库中使用适当的唯一约束。当您在 ruby 中执行此操作时,您会遇到竞争条件。
【讨论】:
这个解决方案的大警告。每当您保存选择时,它将始终保存问题,除非设置了autosave: false
。
@ChrisNicola 你能解释一下你的意思吗,我不明白你的意思。
我的意思是在哪里?如果您的意思是适当的唯一约束,我的意思是向数据库中必须唯一的列/字段添加唯一索引。
实际上是聪明的答案
另外delegate
(在另一个问题中建议)似乎先加载answer
,然后再加载question
。 has_one
使用活动记录并创建连接查询,因此它使用的查询少。【参考方案3】:
belongs_to
关联不能有:through
选项。您最好在 Choice
上缓存 question_id
并向表中添加唯一索引(尤其是因为 validates_uniqueness_of
容易出现竞争条件)。
如果您偏执,请向Choice
添加自定义验证,以确认答案的question_id
匹配,但听起来最终用户永远不应该有机会提交会造成这种不匹配的数据.
【讨论】:
谢谢斯蒂芬,我真的不想直接与 question_id 关联,但我想这是最简单的方法。我最初的想法是,既然“答案”属于“问题”,我总是可以通过“答案”到达“问题”。但是您认为这不容易做到吗,还是您认为这只是一个糟糕的架构? 如果你想要一个唯一的约束/验证,范围字段必须存在于同一个表中。请记住,存在竞争条件。 >> 听起来最终用户永远不应该有机会提交会造成这种不匹配的数据。 -- 你永远不能保证用户“没有机会做某事”,除非你做一个明确的服务器端检查。【参考方案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 从数据库加载answer
,然后再加载question
。这可以稍后通过预先加载您需要的关联(即c = Choice.first(include: answer: :question)
)来优化,但是,如果这种优化是必要的,那么 stephencelis 的答案可能是一个更好的性能决策。
某些选择是有时间和地点的,我认为这种选择在制作原型时会更好。除非我知道它用于不常见的用例,否则我不会将它用于生产代码。
【讨论】:
【参考方案5】:所以你不能有你想要的行为,但你可以做一些感觉像的事情。你希望能够做到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
这样你现在就可以在Choice上提出问题了
【讨论】:
【参考方案6】:听起来您想要的是一个有很多问题的用户。 这个问题有很多答案,其中之一是用户的选择。
这就是你所追求的吗?
我会按照这些思路建模类似的东西:
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
【讨论】:
【参考方案7】:has_many :choices
创建一个名为 choices
的关联,而不是 choice
。尝试改用current_user.choices
。
有关has_many
魔法的信息,请参阅ActiveRecord::Associations 文档。
【讨论】:
感谢您的帮助 Michael,但是,这是我的错字。我已经在做 current_user.choices。此错误与我想将 belongs_to 分配给用户和问题有关。以上是关于通过关联的belongs_to的主要内容,如果未能解决你的问题,请参考以下文章
以编程方式获取 Rails 4 中的 belongs_to 关联的类