Rails:如何从多态模型中连接或预加载belongs_to关联?

13

我的问题是如何从多态模型中加入belongs_to关联。以下是情况:

opinion.rb

class Opinion < ActiveRecord::Base
    belongs_to :opinionable, :polymorphic => true
    belongs_to :category
end

answer.rb

class Answer < ActiveRecord::Base
    has_many :opinions, :as => :opinionable
end

如何实现以下操作:

Opinion.joins(:opinionable).all

它会抛出以下异常:

ArgumentError: You can't create a polymorphic belongs_to join without specifying the polymorphic class!

如何指定要加入的类?

第二个问题是如何预加载它?

Opinion.preload(:opinionable).all

这样可以正常工作。它将为belongs_to中的每个类执行查询。

但是,如果我想做以下操作:

Opinion.preload(:opinionable => :answer_form).all

因为一个模型具有此关联而另一个模型没有,所以会抛出异常。

那么我该如何做以下操作:

Opinion.preload(:answer => :answer_form, :another_belongs_to_model).all

谢谢,David!

3个回答

16

实际上如果你只是这样做:

belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer", conditions: { opinions: { opinionable_type: "Answer"}}

那么你可以这样做

Opinion.joins(:opinionable_answer).where(answers: { awesome: true})

谢谢!正常工作 :) - JCorcuera

14

看起来您的 Opinion 模型尚未指定 opinionable_type:string 列。

请尝试按照以下方式更新您的迁移:

class CreateOpinions < ActiveRecord::Migration
  def self.up
    create_table :opinions do |t|
      t.integer :opinionable_id
      t.string  :opinionable_type

      # ... other fields

      t.timestamps
    end
  end

  def self.down
    drop_table :opinions
  end
end

这将解决你的第二个问题,Opinion.preload(:opinionable).all应该可以很好地工作。
你不能在多态关联上进行连接,因为它们可能位于不同的表中,在Opinion模型加载后检测到。这就是为什么模型需要列opinionable_type
如果你尝试这样做,你会得到下一个异常:

ActiveRecord::EagerLoadPolymorphicError: 无法急切加载多态关联:opinionable

更新:添加了神奇的连接 ^_^
class Opinion < ActiveRecord::Base
  belongs_to :opinionable, :polymorphic => true

  belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer"

  scope :by_type, lambda { |type| joins("JOIN #{type.table_name} ON #{type.table_name}.id = #{Opinion.table_name}.opinionable_id AND #{Opinion.table_name}.opinionable_type = '#{type.to_s}'") }
end

例子:

Opinion.by_type(Answer).to_sql
  => "SELECT \"opinions\".* FROM \"opinions\" JOIN answers ON answers.id = opinions.opinionable_id AND opinions.opinionable_type = 'Answer'" 

多态对我来说很有效(在表中有id和type列)。我明白你为什么说联接不起作用的原因了。我漏掉了模型不知道属于哪些类的事实。 - Schovi
有个想法。如果我知道 Opinion 类的 Opinion.joins([:opinionable, Answer]) 这样的东西是可能的吗? - Schovi
如果您知道具体的类,可以在 Opinion 模型中添加以下内容:belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer"。然后 Opinion.joins(:opinionable_answer) 应该可以正常工作。 - Viacheslav Molokov
这段代码无法工作,因为它会按照另一个相关的模型的id来连接Answer表。查询语句是SELECT opinions.* FROM opinions INNER JOIN answers ON answers.id = opinions.opinionable_id ,但我需要添加连接操作JOIN ... ON ... AND opinions.opinionable_type = 'Answer' - Schovi

0

我知道这个问题很老了,但我刚刚花了一个小时寻找类似问题的解决方案(Rails 3),唯一让我成功的方法是在这里提到的解决方案:https://dev59.com/LGEh5IYBdhLWcg3w7nWW#25966630

在你的情况下,解决方案如下:

class Opinion < ActiveRecord::Base
  # The true polymorphic association
  belongs_to :opinionable, polymorphic: true

  # The trick to solve this problem
  has_one :self_ref, :class_name => self, :foreign_key => :id

  has_one :answer, :through => :self_ref, :source => :opinionable, :source_type => Answer
end

看起来比较棘手,但这样你就能够执行多个链接操作,例如:

joins(answer: :other_model)

并且只要 opinion.opinionable 不是一个 Answeropinion.answer 就会返回 nil

希望能对某人有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接