ActiveRecord - запрос полиморфных ассоциаций - PullRequest
36 голосов
/ 25 марта 2009

Я использую полиморфные ассоциации для отслеживания комментариев в моем проекте. Все очень простые вещи.

Проблема, с которой я столкнулся, заключается в запросах, основанных на полиморфной ассоциации, и присоединении модели Comment к ее владельцу.

Итак ...

У меня есть комментарий модели

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

И режим ForumTopics:

class ForumTopic < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

У меня есть несколько других «комментируемых» моделей, которые сейчас не важны. Все это работает.

Я пытаюсь найти все комментарии, принадлежащие форумной теме с заданным условием (в данном случае «featured» == true).

Когда я пытаюсь использовать искатель для объединения моделей:

@comments = Comment.find(:all 
            :joins => :commentable
            :conditions => ["forum_topics.featured = ? ", true] 
            )

Я получаю следующую ошибку:

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

Использование AR "включить синтаксис":

@comments = Comment.find(:all 
            :include => :forum_topics
            :conditions => ["forum_topics.featured = ? ", true] 
            )

возвращается:

Ассоциация с именем forum_topics не найдена; возможно, вы ошиблись?

Если я попытаюсь соединиться с именем таблицы вместо имени ассоциации (строка вместо символа):

@comments = Comment.find(:all,
            :joins => "forum_topics",
            :conditions => ["forum_topics.featured = ? ", true] 
            )

Понятно:

Mysql :: Ошибка: неизвестная таблица 'comments': ВЫБРАТЬ комментарии. FROM comments forum_topics WHERE (forum_topics.featured = 1) *

(Здесь видно, что синтаксис базового запроса полностью отключен, а объединение вообще отсутствует).

Не уверен, что то, что я делаю, возможно, и есть другие способы достижения требуемого результата, но кажется, что должно быть выполнимым.

Есть идеи? Что-то мне не хватает?

Ответы [ 7 ]

31 голосов
/ 25 марта 2009

Argh!

Я думаю, что нашел проблему.

При присоединении через:

@comments = Comment.find(:all,
        :joins => "forum_topics",
        :conditions => ["forum_topics.featured = ? ", true] 
        )

Вам нужно все присоединиться!

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",

Посмотри на все удивительное: http://guides.rubyonrails.org/active_record_querying.html#joining-tables

25 голосов
/ 10 августа 2015

Старый вопрос, но есть более чистый способ достижения этого путем установки прямой связи для определенного типа наряду с полиморфным:

#comment.rb
class Comment < ActiveRecord::Base

  belongs_to :commentable, polymorphic: true
  belongs_to :forum_topics, -> { where( comments: { commentable_type: 'ForumTopic' } ).includes( :comments ) }, foreign_key: 'commentable_id'

  ...

end

Затем вы можете передать :forum_topics на includes, избавляя от необходимости грязного соединения:

@comments = Comment
  .includes( :forum_topics )
  .where( :forum_topics => { featured: true } )

После этого вы можете очистить этот запрос, переместив запрос в область действия:

#comment.rb
class Comment < ActiveRecord::Base

  ...

  scope :featured_topics, -> { 
    includes( :forum_topics )
    .where( :forum_topics => { featured: true } ) 
  }

  ...

end

Предоставляя вам возможность просто сделать

@comments = Comment.featured_topics
15 голосов
/ 10 апреля 2014

Вам нужны условные, плюс рельсы 3 +

Многие люди ссылались на это в ответах и ​​комментариях, но я чувствовал, что люди, в том числе и я, были бы сбиты с толку, если бы я попал сюда и не читал достаточно внимательно.

Итак, вот правильный ответ, включая условие, которое абсолютно необходимо.

@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
                   .where( comments:     { commentable_type: 'ForumTopic' } )
                   .where( forum_topics: { featured:         true         } )

Спасибо всем, особенно @Jits, @Peter и @prograils за их комментарии.

15 голосов
/ 11 августа 2009

Принятое решение не работает, как только вы вводите другую модель, которая имеет ассоциацию, использующую «commentable». commentable_id не является уникальным, и поэтому вы начнете получать неправильные комментарии.

Например:

Вы решили добавить новостную модель, которая принимает комментарии ...

class News < ActiveRecord::Base
   has_many :comments, :as => :commentable
end

Теперь вы можете получить две записи назад, если вы сделали комментарий на forum_topic с идентификатором 1 и новостной статьей с идентификатором 1, используя ваш запрос:

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"

Вероятно, вы могли бы решить эту проблему, указав тип commentable_type в качестве одного из ваших условий, но я не думаю, что это лучший способ решить эту проблему.

10 голосов
/ 23 февраля 2012

Я наткнулся на этот пост, и он привел меня к моему решению. Использование commentable_type в качестве одного из моих условий, но использование LEFT OUTER JOIN Таким образом, темы форума без комментариев будут включены.

LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
7 голосов
/ 20 июля 2017

Проверено для работы под Рельсы 5 :

Решение 1:

@comments = Comment
              .where(commentable_type: "ForumTopic")
              .joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id")
              .where(forum_topics: {featured: true})
              .all

или

Решение 2:

@comments = Comment
              .joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id AND comments.commentable_type = 'ForumTopic'")
              .where(forum_topics: {featured: true}).all

Обратите внимание на необработанный синтаксис SQL: обратные пометки запрещены. См http://guides.rubyonrails.org/active_record_querying.html#joining-tables.

Лично я предпочитаю решение 1, так как оно содержит меньше необработанного синтаксиса SQL.

0 голосов
/ 10 апреля 2019

Rails по умолчанию не включает полиморфное соединение, но этот драгоценный камень поможет вам с легкостью присоединиться к вашим полиморфным отношениям. https://github.com/jameshuynh/polymorphic_join

...