Rails从多态模型加入或预加载belongs_to关联

Sch*_*ovi 13 polymorphism activerecord join ruby-on-rails preload

我的问题是以下.如何从多态模型加入belongs_to关联

有情况

opinion.rb

class Opinion < ActiveRecord::Base
    belongs_to :opinionable, :polymorphic => true
    belongs_to :category
end
Run Code Online (Sandbox Code Playgroud)

answer.rb

class Answer < ActiveRecord::Base
    has_many :opinions, :as => :opinionable
end
Run Code Online (Sandbox Code Playgroud)

我该怎么办?

Opinion.joins(:opinionabe).所有

它会抛出

ArgumentError:如果不指定多态类,则无法创建多态的belongs_to连接!

我如何具体说明我想加入哪个班级?

第二个问题.如何预加载?

Opinion.preload(:opinionable).所有

工作良好.它将对belongs_to中的每个类进行查询.

但.如果我想做点什么的话

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

有问题,因为一个模型有这种关联,第二个没有.所以它会抛出异常.

那我怎么能做点什么呢

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

谢谢,大卫!

小智 15

其实如果你这样做的话

belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer", conditions: { opinions: { opinionable_type: "Answer"}}
Run Code Online (Sandbox Code Playgroud)

那么你可以做到

Opinion.joins(:opinionable_answer).where(answers: { awesome: true})
Run Code Online (Sandbox Code Playgroud)


Via*_*kov 14

看起来您没有opinionable_type:string为Opinion模型指定列.

尝试以这种方式更新您的迁移:

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
Run Code Online (Sandbox Code Playgroud)

这将解决你的第二个问题,Opinion.preload(:opinionable).all应该运作良好.

您不能在多态关联上进行连接,因为它们可以位于不同的表中,这些表在Opinion加载模型后检测到.这就是为什么模型需要列opinionable_type.

如果您尝试这样做,您将获得下一个例外

ActiveRecord::EagerLoadPolymorphicError:不能急切地加载多态关联 :opinionable

UPD:添加魔术连接^ _ ^

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
Run Code Online (Sandbox Code Playgroud)

例:

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