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!
精彩评论