I have 4 models: Model 1 associated with model 2, and model 2 with model 3. Models 2 and 3 are associated to model 4 with a polymorphic association. This all works. I'm having trouble specifying the association between model 1 and model 4, which are thus related in two ways:
Below you'll find all the associations, except that between model 1 and model 4:
Author model:
has_many :books, dependent: :destroy
Book model:
belongs_to :author
has_many :chapters, dependent: :destroy
has_many :titles, as: :titlesource, # Polymorphic Association
dependent: :destroy
Chapter model:
belongs_to :book
has_many :titles, as: :titlesource, # Polymorphic Association
dependent: :destroy
Title model:
belongs_to :titlesource, polymorphic: true
Question: Suppose you have the above associations and you want to know the titles
that an author
has, irrespective whether they are a book-title or chapter-title. What associations should I expand the above setup with? (I assume something with has_many through
)
Update: If it were only book-titles I would be looking for, I would expect adding has_many :titles, through: :books
to the Author
model to work. But it doesn't. If I add that association, then Author.first.titles
produces the error undefined method 'titles'
. I think this has to do with the polymorphic association and naming...? What am I doing wrong?
For chapter title it is even more difficult. That association goes through the Book
model, then through the Chapter
model and then to the Title
model. How can I include these titles in the author model? I think it would have to be code with the following logic (but obviously this code is incorrect):
has_many :titles, through: :books && :books:chapters
Update: The two answers given use SQL. Do these solutions not send a lot of queries to the db, causing problems if the method is used often? Has one of these methods an advantage over the other in performance?
If you were to use an association on the Author
like has_many :titles, through: :books
it should normally work as long as you have set a titlesource_id
and a titlesource_type
on the Title
model migration.
However, if you would add another one to fetch the titles through the chapters like this has_many :titles, through: :chapters
it would overwrite the first one and it would fetch only the titles that are of titlesource_type = 'Chapter'
.
Therefore the solution that I would suggest to you, if you don't mind sql is to implement a method like this in the Author model.
def titles
Title.where(
"(titles.titlesource_type = 'Book'
AND titles.titlesource_id
IN (SELECT books.id FROM books WHERE books.author_id = #{id}))
OR (titles.titlesource_type = 'Chapter'
AND titles.titlesource_id
IN (SELECT chapters.id
FROM books
INNER JOIN chapters ON chapters.book_id = books.id
WHERE books.author_id = #{id}))"
)
end
If you don't need ActiveRecord methods (like where
or order
), then this will give you an array of titles.
def titles_array # sacrificing ActiveRecord methods
books.collect { |b| b.titles + b.chapters.collect { |c| c.titles } }
end
You can still use AR methods on the individual array elements such as titles_array.first.author
, etc
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.