如何通过关系显示 has_many 中的唯一记录?

我想知道通过 Rails3中的关系来显示来自 has _ many 的唯一记录的最佳方式是什么。

我有三个模型:

class User < ActiveRecord::Base
has_many :orders
has_many :products, :through => :orders
end


class Products < ActiveRecord::Base
has_many :orders
has_many :users, :through => :orders
end


class Order < ActiveRecord::Base
belongs_to :user, :counter_cache => true
belongs_to :product, :counter_cache => true
end

假设我想在展示页面上列出客户订购的所有产品。

他们可能已经多次订购了某些产品,所以我使用 counter _ cache 根据订单数量按降序显示。

但是,如果他们已经订购了一个产品多次,我需要确保每个产品只列出一次。

@products = @user.products.ranked(:limit => 10).uniq!

当一个产品有多个订单记录时可以工作,但是如果一个产品只订购了一次就会产生错误。(rank 是在其他地方定义的自定义排序函数)

另一种选择是:

@products = @user.products.ranked(:limit => 10, :select => "DISTINCT(ID)")

我不确定我的方法是否正确。

还有别人解决过这个问题吗?你遇到了什么问题?我在哪里可以找到更多关于。独一无二!还有什么区别?

What is the best way to generate a list of unique records through a has_many, through relationship?

Thanks

51304 次浏览

是否尝试在 has _ many 关联上指定: uniq 选项:

has_many :products, :through => :orders, :uniq => true

来自 Rails 文档:

:uniq

如果为 true,则从集合中省略重复的内容。

铁路更新4:

在 Rails4中,不推荐使用 has_many :products, :through => :orders, :uniq => true。相反,您现在应该编写 has_many :products, -> { distinct }, through: :orders。有关更多信息,请参见 通过 ActiveRecord Associations 文档中的关系来获得 has _ many: : 的独特部分。感谢 Kurt Mueller 在他的评论中指出了这一点。

You could use group_by. For example, I have a photo gallery shopping cart for which I want order items to be sorted by which photo (each photo can be ordered multiple times and in different size prints). This then returns a hash with the product (photo) as the key and each time it was ordered can be listed in context of the photo (or not). Using this technique, you could actually output an order history for each given product. Not sure if that's helpful to you in this context, but I found it quite useful. Here's the code

OrdersController#show
@order = Order.find(params[:id])
@order_items_by_photo = @order.order_items.group_by(&:photo)

@order_items_by_photo看起来像这样:

=> {#<Photo id: 128>=>[#<OrderItem id: 2, photo_id: 128>, #<OrderItem id: 19, photo_id: 128>]

所以你可以这样做:

@orders_by_product = @user.orders.group_by(&:product)

然后当你在视图中看到这个时,只需要循环这样的东西:

- for product, orders in @user.orders_by_product
- "#{product.name}: #{orders.size}"
- for order in orders
- output_order_details

This way you avoid the issue seen when returning only one product, since you always know that it will return a hash with a product as the key and an array of your orders.

对于您正在尝试做的事情来说,这可能有点过分,但是它确实给了您一些不错的选择(例如,订购的日期等) ,除了数量之外还可以使用。

注意,从 Rails 4开始,uniq: true已经从 has_many的有效选项中删除了。

在 Rails 4中,您必须提供一个范围来配置这种行为。望远镜可以通过 lambda 提供,像这样:

has_many :products, -> { uniq }, :through => :orders

Rails 指南涵盖了这个和其他你可以使用作用域来过滤关系查询的方法,滚动到4.3.3部分:

Http://guides.rubyonrails.org/association_basics.html#has-many-association-reference

在 Rails6上,我完美地做到了这一点:

  has_many :regions, -> { order(:name).distinct }, through: :sites

我找不到其他的答案。

在 Rails6中使用范围内的 -> { distinct }它将工作

class Person
has_many :readings
has_many :articles, -> { distinct }, through: :readings
end
 

person = Person.create(name: 'Honda')
article   = Article.create(name: 'a1')
person.articles << article
person.articles << article
person.articles.inspect # => [#<Article id: 7, name: "a1">]
Reading.all.inspect     # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]