Rails соединяется или предварительно загружается принадлежностью к ассоциации из полиморфной модели

моя проблема следующая. Как я могу присоединить принадлежность к ассоциации из полиморфной модели

Есть ситуация

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(: opinionabe).all

он бросит

ArgumentError: вы не можете создать полиморфную принадлежность для соединения без указания полиморфного класса!

Как я могу определить, к какому классу я хочу присоединиться?

Второй вопрос. Как его предварительно загрузить?

Opinion.preload(: opinionable).all

работает отлично. Он будет выполнять запрос для каждого класса в role_to.

Но. если я хочу сделать что-то вроде

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

есть проблема, потому что одна модель имеет эту связь, а вторая - нет. Таким образом, это будет исключение.

Итак, как я могу сделать что-то вроде

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

?

Спасибо, Дэвид!

Ответы

Ответ 1

Похоже, вы не указали столбец 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

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

Пример:

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

Ответ 2

Собственно, если вы просто делаете

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

то вы можете сделать

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

Ответ 3

Я знаю, что этот вопрос старый, но я потратил час на поиск решения аналогичной проблемы (Rails 3), и единственный способ заставить его работать - это решение, указанное здесь: fooobar.com/info/411923/...

Который в вашем случае будет:

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 не является Answer, opinion.answer возвращает nil.

Надеюсь, это кому-нибудь поможет!