简体   繁体   English

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

[英]Rails joins or preload belongs_to association from polymorphic model

my problem is following.我的问题如下。 How can I joins belongs_to association from polymorphic model如何从多态 model 加入belongs_to 关联

There is situation有情况

opinion.rb意见.rb

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

answer.rb答案.rb

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

How can i do following我该怎么做

Opinion.joins(:opinionabe).all Opinion.joins(:opinonabe).all

it will throw它会抛出

ArgumentError: You can't create a polymorphic belongs_to join without specifying the polymorphic class! ArgumentError: 你不能在不指定多态类的情况下创建多态belongs_to join!

How can i specific which class i want to join?我如何具体说明我想加入哪个 class?

Second question.第二个问题。 How to preload it?如何预加载?

Opinion.preload(:opinionable).all Opinion.preload(:opinionable).all

works fine.工作正常。 It will do query for each class in belongs_to.它将查询belongs_to 中的每个class。

But.但。 if i want to do something like如果我想做类似的事情

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

there is problem because one model has this association and second hasn't.有问题,因为一个 model 有这个关联,而第二个没有。 So it will throw exception.所以它会抛出异常。

So how i can do something like所以我怎么能做类似的事情

Opinion.preload(:answer =>:answer_form, :another_belongs_to_model).all 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.您似乎没有为您的意见 model 指定 opinionable_type opinionable_type:string列。

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.这将解决您的第二个问题,并且Opinion.preload(:opinionable).all应该可以正常工作。

You cann't do joins on polymorphic association because they can be located in different tables, which are detected after Opinion model is loaded.您不能对多态关联进行连接,因为它们可以位于不同的表中,这些表是在加载Opinion model 后检测到的。 That why model needs column opinionable_type .这就是为什么opinionable_type需要专栏 opinionable_type 的原因。

If you try to do this you'll get next exception如果您尝试这样做,您将获得下一个异常

ActiveRecord::EagerLoadPolymorphicError : Can not eagerly load the polymorphic association :opinionable ActiveRecord::EagerLoadPolymorphicError :不能急切地加载多态关联:opinionable opinionable

UPD: Added magic join ^_^ 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

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我知道这个问题很老,但我只花了一个小时寻找类似问题的解决方案(Rails 3),我让它工作的唯一方法是这里说明的解决方案: 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) . joins(answer: :other_model)

And whenever opinion.opinionable is not an Answer , opinion.answer will return nil .并且只要opinion.opinionable不是Answer ,则opinion.answer将返回nil

Hope it helps somebody!希望它可以帮助某人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM