ActiveRecord-querying polymorphic associations
Używam skojarzeń polimorficznych do śledzenia komentarzy w moim projekcie. Wszystko bardzo prosto.
Problem, który mam polega na zapytaniu w oparciu o skojarzenie polimorficzne i połączeniu z modelu komentarza z powrotem do jego właściciela.
Więc ...
Mam Model komentarza
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
I tryb ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Mam kilka innych" komentowalnych " modeli, które nie są teraz ważne. To wszystko działa.
To, co próbuję zrobić, to znaleźć wszystkie komentarze, które należą do ForumTopic z określonym warunkiem (w tym przypadku 'featured' = = true).
Kiedy próbuję użyć Findera, aby dołączyć do modeli:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
Otrzymuję następujący błąd:
nie można z zapałem załadować asocjacji polimorficznej: komentować
Użycie AR "include syntax":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
Zwraca:
Stowarzyszenie o nazwie "forum_topics" nie zostało znalezione; być może źle napisałeś to?
Jeśli spróbuję połączyć się z nazwą tabeli zamiast nazwy asocjacji (łańcuch zamiast symbolu):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Widzę:
Mysql:: Error: Unknown table 'comments': SELECT comments. FROM comments forum_topics WHERE (forum_topics.featured = 1) *
(możesz zobaczyć tutaj, że składnia podstawowego zapytania jest całkowicie wyłączona, a join całkowicie brakuje).
Nie wiem, czy to, co robię, jest w ogóle możliwe, a tam są inne sposoby osiągnięcia wymaganego rezultatu, ale wydaje się, że powinno być wykonalne. Jakieś pomysły? Coś mi umknęło?6 answers
Argh!
Chyba znalazłem problem.Podczas łączenia przez:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Potrzebujesz całego połączenia!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Zobacz the ever-awesome: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-25 03:59:40
Stare pytanie, ale jest czystszy sposób na osiągnięcie tego poprzez ustanowienie bezpośredniego związku dla określonego typu wraz z polimorficznym:
#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
Następnie możesz przejść :forum_topics
do includes
pozbycie się potrzeby niechlujnego łączenia:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
Możesz następnie dalej to wyczyścić, przenosząc zapytanie do zakresu:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
Zostawiając cię, abyś mógł po prostu zrobić
@comments = Comment.featured_topics
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-10 07:31:02
Przyjęte rozwiązanie nie działa po wprowadzeniu innego modelu, który ma skojarzenie z "komentowalnym". commentable_id nie jest unikalny i dlatego zaczniesz pobierać złe komentarze.
Na przykład:
Decydujesz się dodać model newsa, który akceptuje komentarze...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Teraz możesz dostać dwa rekordy z powrotem, jeśli zrobiłeś komentarz na forum_topic o id 1 i artykuł newsa o id 1 używając zapytania:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Prawdopodobnie mógłbyś Rozwiąż problem, dostarczając commentable_type jako jeden z warunków, ale myślę, że nie jest to najlepszy sposób podejścia do tego problemu.
Potrzebujesz warunkowego, Plus Rails 3 +
Wiele osób nawiązywało do tego w odpowiedziach i komentarzach, ale czułem, że ludzie, w tym ja, potkną się, jeśli wyląduję tutaj i nie przeczytam wystarczająco dokładnie.
Oto właściwa odpowiedź, w tym warunkowa, która jest absolutnie niezbędna.
@comments = Comment.joins( "INNER JOIN `forum_topics` ON `comments`.`commentable_id` = `forum_topics`.`id`" )
.where( :comments => { commentable_type: 'ForumTopic' } )
.where( :forum_topics => { featured: true } )
Rails 5 +
Dla Rails 5+, musisz usunąć tylne kleszcze w łańcuchu zapytania:
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Dzięki wszystkim, szczególnie @Jits, @ Peter i @prograils za komentarze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-25 16:55:46
Natknąłem się na ten post i to doprowadziło mnie do mojego rozwiązania. Używam commentable_type jako jednego z moich warunków, ale zamiast tego używam lewego zewnętrznego łącznika. W ten sposób zostaną uwzględnione tematy forum bez komentarzy.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 19:07:45
Sprawdzone do pracy pod Rails 5:
Rozwiązanie 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
Lub
Rozwiązanie 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
Zwróć uwagę na surową składnię SQL: żadne backticki nie są dozwolone. Zobacz http://guides.rubyonrails.org/active_record_querying.html#joining-tables .
Osobiście wolę rozwiązanie 1, ponieważ zawiera mniej surowej składni SQL.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-29 06:14:49