Ruby-on-rails 如何通过关系显示 has_many 中的唯一记录?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5846638/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to display unique records from a has_many through relationship?
提问by Andy Harvey
I'm wondering what is the best way to display unique records from a has_many, through relationship in Rails3.
我想知道通过 Rails3 中的关系显示来自 has_many 的唯一记录的最佳方式是什么。
I have three models:
我有三个模型:
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
Lets say I want to list all the products a customer has ordered on their show page.
假设我想列出客户在其展示页面上订购的所有产品。
They may have ordered some products multiple times, so I'm using counter_cache to display in descending rank order, based on the number of orders.
他们可能多次订购了某些产品,因此我使用 counter_cache 根据订单数量以降序显示。
But, if they have ordered a product multiple times, I need to ensure that each product is only listed once.
但是,如果他们多次订购产品,我需要确保每个产品只列出一次。
@products = @user.products.ranked(:limit => 10).uniq!
works when there are multiple order records for a product, but generates an error if a product has only been ordered once. (ranked is custom sort function defined elsewhere)
当一个产品有多个订单记录时有效,但如果一个产品只订购了一次,则会产生错误。(排名是在别处定义的自定义排序函数)
Another alternative is:
另一种选择是:
@products = @user.products.ranked(:limit => 10, :select => "DISTINCT(ID)")
I'm not confident that I'm on the right approach here.
我不相信我在这里采取了正确的方法。
Has anyone else tackled this? What issues did you come up against? Where can I find out more about the difference between .unique! and DISTINCT()?
有没有其他人解决过这个问题?你遇到了哪些问题?我在哪里可以找到有关 .unique 之间差异的更多信息!和 DISTINCT()?
What is the best way to generate a list of unique records through a has_many, through relationship?
通过 has_many, through 关系生成唯一记录列表的最佳方法是什么?
Thanks
谢谢
回答by mbreining
Have you tried to specify the :uniq option on the has_many association:
您是否尝试在 has_many 关联上指定 :uniq 选项:
has_many :products, :through => :orders, :uniq => true
From the Rails documentation:
从Rails 文档:
:uniqIf true, duplicates will be omitted from the collection. Useful in conjunction with :through.
:uniq如果为 true,将从集合中省略重复项。与 :through 结合使用很有用。
UPDATE FOR RAILS 4:
轨道 4 的更新:
In Rails 4, has_many :products, :through => :orders, :uniq => trueis deprecated. Instead, you should now write has_many :products, -> { distinct }, through: :orders. See the distinct section for has_many: :through relationships on the ActiveRecord Associations documentationfor more information. Thanks to Kurt Mueller for pointing this out in his comment.
在 Rails 4 中,has_many :products, :through => :orders, :uniq => true已弃用。相反,您现在应该编写has_many :products, -> { distinct }, through: :orders. 有关详细信息,请参阅ActiveRecord 关联文档中 has_many::through 关系的不同部分。感谢 Kurt Mueller 在他的评论中指出这一点。
回答by Yoshiki
Note that uniq: truehas been removed from the valid options for has_manyas of Rails 4.
请注意,uniq: true从has_manyRails 4开始,它已从有效选项中删除。
In Rails 4 you have to supply a scope to configure this kind of behavior. Scopes can be supplied through lambdas, like so:
在 Rails 4 中,您必须提供一个范围来配置这种行为。范围可以通过 lambdas 提供,如下所示:
has_many :products, -> { uniq }, :through => :orders
The rails guide covers this and other ways you can use scopes to filter your relation's queries, scroll down to section 4.3.3:
rails 指南涵盖了使用范围过滤关系查询的这种方式和其他方式,向下滚动到第 4.3.3 节:
http://guides.rubyonrails.org/association_basics.html#has-many-association-reference
http://guides.rubyonrails.org/association_basics.html#has-many-association-reference
回答by Josh Kovach
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
你可以使用group_by. 例如,我有一个照片库购物车,我希望订购的商品按照片排序(每张照片可以多次订购并以不同尺寸打印)。然后返回一个以产品(照片)为键的散列,每次订购时都可以在照片的上下文中列出(或不列出)。使用这种技术,您实际上可以输出每个给定产品的订单历史记录。不确定这在这种情况下是否对您有帮助,但我发现它非常有用。这是代码
OrdersController#show
@order = Order.find(params[:id])
@order_items_by_photo = @order.order_items.group_by(&:photo)
@order_items_by_photothen looks something like this:
@order_items_by_photo然后看起来像这样:
=> {#<Photo id: 128>=>[#<OrderItem id: 2, photo_id: 128>, #<OrderItem id: 19, photo_id: 128>]
So you could do something like:
所以你可以这样做:
@orders_by_product = @user.orders.group_by(&:product)
Then when you get this in your view, just loop through something like this:
然后,当您在视图中看到它时,只需循环执行以下操作:
- 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.
通过这种方式,您可以避免只返回一个产品时出现的问题,因为您总是知道它将返回一个以产品为键的哈希值和一个订单数组。
It might be overkill for what you're trying to do, but it does give you some nice options (i.e. dates ordered, etc.) to work with in addition to the quantity.
对于您想要做的事情来说,这可能有点矫枉过正,但它确实为您提供了一些不错的选择(即订购的日期等),除了数量之外。
回答by Sean Mitchell
On Rails 6 I got this to work perfectly:
在 Rails 6 上,我让它完美地工作:
has_many :regions, -> { order(:name).distinct }, through: :sites
I couldn't get any of the other answers to work.
我无法得到任何其他的工作答案。

