如何从多态关联中的关联模型中提取不同的列值?

时间:2017-06-26 19:58:30

标签: ruby-on-rails polymorphic-associations distinct-values

如果我在3个模型之间有多态关联:

评论

belongs_to :book, :class_name => 'Book', :foreign_key => 'ref_id', conditions: "comments.ref_type = 'Book'"
belongs_to :article, :class_name => 'Article', :foreign_key => 'ref_id', conditions: "comments.ref_type = 'Article'"
belongs_to :ref, :polymorphic => true

对于给定的评论列表,如何从TitleBook模型的Article列中选择不同的值?

例如,如果我必须列出书籍的标题和在一段时间内给出评论的文章,​​那我该怎么办呢?我可以轻松选择评论列表,但如何从BookArticle中选择相关的唯一标题?

例如:

Book
+--------------+
| Id |  Title  |
+----+---------+
| 1  | 'Book1' | 
| 2  | 'Book2' |
| 3  | 'Book3' |
+--------------+

Article
+-----------------+
| Id |   Title    |
+----+------------+
| 1  | 'Article1' |
| 2  | 'Article2' |
+-----------------+

Comments
+--------------------------------------+
| Id |  comment   | ref_id | ref_type  |
+----+------------+--------+-----------+
| 1  | 'comment1' |   1    |   Book    | 
| 2  | 'comment2' |   1    |   Book    | 
| 3  | 'comment3' |   1    |   Article | 
| 4  | 'comment4' |   3    |   Book    | 
+--------------------------------------+

我需要标题列表为'Book1''Book3''Article1'

4 个答案:

答案 0 :(得分:2)

简单方法:

{
  "files.exclude": {
    "**/.git": true,
    "**/.DS_Store": true,
    "**/*.js.map": true,
    "**/*.js": {"when": "$(basename).ts"}
  }
}

获取所有评论,急切加载其引用,并返回包含唯一标题的数组。急切的装载部件并非严格必要,但可能表现更好。 执行3个查询,一个用于注释,一个用于每种ref。 您可以将 all 替换为任何范围。 请注意,这将获取所有注释及其引用,并使用ruby将其转换为数组,而不是SQL。这很有效,但性能可能会受到影响。通常最好使用 distinct 来获取唯一值,并使用 pluck 来获取这些值的数组。

这种方法适用于各种参考。因此,如果我们引入第三种参考,例如发布,它将自动包含在此查询中。

可重复使用的方式:

Comment.all.includes(:ref).map { |comment| comment.ref.title }.uniq

此范围使用arel和子查询的UNION来获取标题。 它基本上将ref的标题添加到注释对象。由于范围应该是可链接的,因此它返回ActiveRecord Relation而不是数组。要获得不同的标题,请附加 distinct.pluck(:title)

class Comment < ApplicationRecord
  belongs_to :book, class_name: 'Book', foreign_key: 'ref_id'
  belongs_to :article, class_name: 'Article', foreign_key: 'ref_id'
  belongs_to :ref, polymorphic: true

  scope :with_ref_titles, lambda {
    book_titles = select('comments.*, books.title').joins(:book)
    article_titles = select('comments.*, articles.title').joins(:article)
    union = book_titles.union(article_titles)
    comment_table = arel_table
    from(comment_table.create_table_alias(union, :comments).to_sql)
  }
end

此范围产生的SQL查询如下所示:

comments = Comment.with_ref_titles
comments.distinct.pluck(:title) # => ["Article1", "Book1", "Book3"]
comments.where('title LIKE "Book%"').distinct.pluck(:title) # => ["Book1", "Book3"]

使用rails 5.1.2和sqlite进行测试。

答案 1 :(得分:1)

我认为最直接使用Arel API或迭代数组的最简单方法是在.titles_for_commentsBook上定义Article范围在给定comments的集合时,从每个表中选择不同的标题。然后为同时使用.distinct_titles的{​​{1}}定义Comment范围。例如,下面的模型和范围定义允许您通过调用[Book|Article].titles_for_comments找到任何给定Comment::ActiveRecord_Relation实例的不同的书籍和文章标题

Comment.distinct_titles

您可以下载此Gist并使用class Book < ActiveRecord::Base has_many :comments, as: :ref def self.titles_for_comments(comment_ids) joins(:comments).where(comments: { id: comment_ids }).distinct.pluck(:title) end end class Article < ActiveRecord::Base has_many :comments, as: :ref def self.titles_for_comments(comment_ids) joins(:comments).where(comments: { id: comment_ids }).distinct.pluck(:title) end end class Comment < ActiveRecord::Base belongs_to :ref, polymorphic: true def self.distinct_titles comment_ids = ids Article.titles_for_comments(comment_ids) + Book.titles_for_comments(comment_ids) end end 运行该测试,测试应通过https://gist.github.com/msimonborg/907eb513fdde9ab48ee881d43ddb8378

答案 2 :(得分:1)

试试这个

  <div *ngFor="let item of active_orders">
    <div class="row">
      <div class="col-md-12">
        <div class="col-md-4"  *ngFor="let detail of item.products">
          <h4>
            {{detail.name}}
          </h4>
        </div>
      </div>
    </div>
  </div>

答案 3 :(得分:1)

我建议您查看本教程:http://karimbutt.github.io/blog/2015/01/03/step-by-step-guide-to-polymorphic-associations-in-rails/并查看它是否可行。

如果您控制模型代码,我会设置注释以使其属于通用可注释对象。例如:

class Comment < ActiveRecord::Base
  belong_to :commentable, :polymorphic => true
end

class Book < ActiveRecord::Base
  has_many :comments, as: commentable
end

class Article < ActiveRecord::Base
  has_many :comments, as: commentable
end

然后给出任何一组评论你可以运行

Comments.each do |comment|
  comment.commentable.title
end.uniq

目前这可能看起来要花很多工作,但如果你坚持使用这个项目,我希望书籍和文章可以共享这类代码的很多,如以及将来可能添加其他可评论的对象。总体而言,拥有通用对象将节省大量工作。