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
.
Надеюсь, это кому-нибудь поможет!