Advanced associations in rails
Right now I'm using a has_and_belongs_to_many association for two of my models like so:
class Books < ActiveRecord::Base
has_and_belongs_to_many :publishers
end
class Publisher < ActiveRecord::Base
belongs_to :publishing_company
has_and_belongs_to_many :books
end
开发者_运维问答
You'll notice that each publisher belongs to a publishing company:
class PublishingCompany < ActiveRecord::Base
has_many :publishers
end
My goal is to set up an association that would allow me to do:
PublishingCompany.find(1).books
Is this possible with conventional RoR associations?
The concept you're looking for is specifying a second degree association using the :through parameter on a has_many association on your PublishingCompany class. Doing second degree associations (that will join 2 extra tables) are very common, I don't think I've ever performed a third degree association though (publishers -> publisher_books -> books), and if I recall right Rails gets fairly sloppy at understanding what you're trying to do once you push associations this far.
The first option that's worth trying is:
class PublishingCompany
has_many :publishers
has_many :books, :through => :publishers
end
The Rails documentation however states that a :through parameter can only be used on a has_many or belongs_to, meaning this shouldn't work through the has_and_belongs_to_many association you have.
Your second options is what I had to do on an early system I wrote on Rails 1. I'll likely get voted down for this, but it was something I had to cook up since I couldn't get rails to handle it.
Since you're only going to use the association in a read only fashion, I just created a fake method to handle it. Be warned, this is a last resort. As a side note I personally dislike has_and_belongs_to_many associations as I find it strange that you don't have objects that you can manipulate that represent the rows of the join table.
class Books
has_many :book_publishers
has_many :publishers, :through => :book_publishers
end
class BookPublisher
belongs_to :book
belongs_to :publisher
end
class Publisher
has_many :book_publishers
has_many :books, :through => :book_publishers
belongs_to :publishing_company
end
class PublishingCompany
has_many :publishers
has_many :book_publishers, :through => :publishers
def books
book_publishers.map{|bp|bp.book}
end
end
# typical use, eager loading to avoid N+1
company = PublishingCompany.first :include => {:book_publishers => :book}
company.books.each {|b| puts b.title}
This should work for you.
class PublishingCompany < ActiveRecord::Base
has_many :books, :through => :publishers, :source => :books
end
:source
parameter is the one you need to use
精彩评论