简体   繁体   English

如果涉及多态关联,如何创建“ has_many through”关联?

[英]How to create a 'has_many through' association if a polymorphic association is involved?

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. 我有4个模型:模型1与模型2相关联,模型2与模型3相关联。模型2和3通过多态关联与模型4相关联。 This all works. 所有这一切。 I'm having trouble specifying the association between model 1 and model 4, which are thus related in two ways: 我在指定模型1和模型4之间的关联时遇到麻烦,因此它们以两种方式关联:

  1. model 1 - model 2 - model 4 模型1-模型2-模型4
  2. model 1 - model 2 - model 3 - model 4 模型1-模型2-模型3-模型4

Below you'll find all the associations, except that between model 1 and model 4: 在下面您将找到所有关联,除了模型1和模型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. 问题:假设您具有上述关联,并且想知道author具有的titles ,而不管它们是书名还是章标题。 What associations should I expand the above setup with? 我应该将上述设置与哪些关联进行扩展? (I assume something with has_many through ) (我通过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. 更新:如果只是我要查找的书名,我希望has_many :titles, through: :bookshas_many :titles, through: :books添加到Author模型中可以正常工作。 But it doesn't. 但事实并非如此。 If I add that association, then Author.first.titles produces the error undefined method 'titles' . 如果我添加了该关联,则Author.first.titles会产生错误的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. 该关联通过Book模型,再通过Chapter模型,再到Title模型。 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. 更新:给出的两个答案使用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. 如果您要在Author上使用has_many :titles, through: :books类的关联has_many :titles, through: :books正常工作,只要您在Title模型迁移中设置了titlesource_idtitlesource_type

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' . 但是,如果您要添加另一个标题以通过诸如has_many :titles, through: :chapters这样的章节来获取has_many :titles, through: :chapters它将覆盖第一个标题,并且仅获取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. 因此,如果您不介意sql,那么我建议您使用的解决方案是在Author模型中实现这样的方法。

  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. 如果您不需要ActiveRecord方法(例如whereorder ),那么这将为您提供一系列标题。

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 您仍然可以在单个数组元素(例如titles_array.first.author等)上使用AR方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM