开发者

Rails joins or preload belongs_to association from polymorphic model

my problem is following. How can I joins belongs_to association from polymorphic model

There is situation

opinion.rb

class Opinion < ActiveRecord::Base
   开发者_如何学C belongs_to :opinionable, :polymorphic => true
    belongs_to :category
end

answer.rb

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

How can i do following

Opinion.joins(:opinionabe).all

it will throw

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

How can i specific which class i want to join?

Second question. How to preload it?

Opinion.preload(:opinionable).all

works fine. It will do query for each class in belongs_to.

But. if i want to do something like

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

there is problem because one model has this association and second hasn't. So it will throw exception.

So how i can do something like

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

?

Thanks, David!


Actually if you just do

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

then you can do

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


It looks like you have not specified opinionable_type:string column for your Opinion model.

Try to update your migration in this manner:

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

This will solve your second question and Opinion.preload(:opinionable).all should work well.

You cann't do joins on polymorphic association because they can be located in different tables, which are detected after Opinion model is loaded. That why model needs column opinionable_type.

If you try to do this you'll get next exception

ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :opinionable

UPD: Added magic join ^_^

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

Example:

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


I know this question is old but I just spent an hour looking for the solution to a similar problem (Rails 3) and the only way I got it to work was the solution stated here: https://stackoverflow.com/a/25966630/6878997

Which, in your case would be:

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

Seems tricky but this way you will be able to do multiple chained joins such as:

joins(answer: :other_model).

And whenever opinion.opinionable is not an Answer, opinion.answer will return nil.

Hope it helps somebody!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜