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

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

我有 4 个模型:模型 1 与模型 2 相关联,模型 2 与模型 3 相关联。模型 2 和模型 3 通过多态关联与模型 4 相关联。这一切都有效。我在指定模型 1 和模型 4 之间的关联时遇到问题,因此它们以两种方式相关:

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

下面是所有关联,除了模型 1 和模型 4 之间的关联:


作者模型:

has_many :books,     dependent: :destroy

图书型号:

belongs_to :author
has_many   :chapters, dependent: :destroy
has_many   :titles,   as: :titlesource,       # Polymorphic Association
                      dependent: :destroy

章节模型:

belongs_to :book
has_many   :titles,   as: :titlesource,       # Polymorphic Association
                      dependent: :destroy

标题模型:

belongs_to :titlesource, polymorphic: true

问题:假设你有上面的联想,你想知道一个author有一个titles,不管它们是不是一个book-title 或 chapter-title。我应该使用哪些关联来扩展上述设置? (我假设有 has_many through

更新: 如果我要找的只是 book-titles,我希望将 has_many :titles, through: :books 添加到 Author 模型中上班。但事实并非如此。如果我添加该关联,则 Author.first.titles 会产生错误 undefined method 'titles'。我认为这与多态关联和命名有关......?我做错了什么?

章节标题就更难了。该关联通过 Book 模型,然后通过 Chapter 模型,然后到达 Title 模型。如何将这些标题包含在作者模型中?我认为它必须是具有以下逻辑的代码(但显然这段代码是不正确的):

has_many :titles, through: :books && :books:chapters

更新:给出的两个答案使用SQL。这些解决方案不会向数据库发送大量查询,如果经常使用该方法会导致问题吗?这些方法中的一种在性能上是否优于另一种?

如果您要在 Author 上使用像 has_many :titles, through: :books 这样的关联,只要您在 titlesource_idtitlesource_type Title 模型迁移。

但是,如果您像这样添加另一个来获取章节中的标题 has_many :titles, through: :chapters,它会覆盖第一个,并且它只会获取 titlesource_type = 'Chapter' 的标题。

因此,如果您不介意,我建议您的解决方案 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

如果您不需要 ActiveRecord 方法(如 whereorder),那么这将为您提供一个标题数组。

def titles_array  # sacrificing ActiveRecord methods
  books.collect { |b| b.titles + b.chapters.collect { |c| c.titles } }
end

您仍然可以对 titles_array.first.author 等单个数组元素使用 AR 方法