通过另一个协会关联的铁路订单?

时间:2013-04-28 03:21:36

标签: ruby-on-rails join associations

举一个简单的例子,假设一家书店有一本书有一位作者。这些书通过订单有很多销售。作者可以有很多书。

我正在寻找一种方法来列出按销售订购的作者。由于销售与书籍有关,而与作者无关,我如何才能实现这一目标?

我猜是这样的: Author.order("sales.count").joins(:orders => :sales)

但是返回一个列无法找到错误。

我已经能够通过在作者模型中定义它来连接它们。以下显示正确的销售计数,但它确实为每个作者ping数据库......不好。我更愿意加载它们,但我似乎无法让它正常工作,因为如果我删除self.id并将连接分配给@authors,它将不会列出任何碰巧有0销售的作者。 / p>

    class Author < ActiveRecord::Base

      def sales_count
        Author.where(id: self.id).joins(:orders => :sales).count
      end
    end

更具体地说,如何通过计数结果对它们进行排序,以便我能够首先列出最受欢迎的作者?

1 个答案:

答案 0 :(得分:2)

首先,让我们在Author类本身上拥有所有关联,以保持查询代码的简单。

class Author < AR::Base
  has_many :books
  has_many :orders, :through => :books
  has_many :sales, :through => :orders
end

最简单的方法是让groupcount一起使用,它会以{author-id: count}形式为您提供哈希:

author_counts = Author.joins(:sales).group("authors.id").count
=> {1 => 3, 2 => 5, ... }

您现在可以使用author_counts哈希对作者进行排序并查找计数(没有销售的作者将返回nil):

<% Author.all.sort_by{|a| author_counts[a.id] || 0}.reverse.each do |author| %>
  <%= author.name %>: <%= author_counts[author.id] || 0 %>
<% end %>

<强>更新

另一种方法是使用ar_outer_joins gem,它可以解决使用includes生成LEFT JOIN的限制:

authors = Author.outer_joins(:sales).
                 group(Author.column_names.map{|c| "authors.#{c}").
                 select("authors.*, COUNT(sales.id) as sales_count").
                 order("COUNT(sales.id) DESC")

现在您的视图可能如下所示:

<% authors.each do |author| %>
  <%= author.name %>: <%= author.sales_count %>
<% end %>

此示例演示了LEFT JOIN在您不能(或特别不希望)急切加载其他关联的情况下的用处。我不知道默认情况下为什么outer_joins未包含在ActiveRecord中。