我想知道在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!
在产品有多个订单记录时有效,但如果产品只订购一次,则会产生错误。 (排名是在别处定义的自定义排序函数)
另一种选择是:
@products = @user.products.ranked(:limit => 10, :select => "DISTINCT(ID)")
我不相信我在这里采取了正确的方法。
还有其他人解决了这个问题吗?你遇到了什么问题?我在哪里可以找到更多关于.unique之间的区别!和DISTINCT()?
通过has_many,通过关系生成唯一记录列表的最佳方法是什么?
由于
答案 0 :(得分:218)
您是否尝试在has_many关联上指定:uniq选项:
has_many :products, :through => :orders, :uniq => true
:uniq
如果为true,将从集合中省略重复项。与以下内容结合使用:通过。
铁路更新4:
在Rails 4中,不推荐使用has_many :products, :through => :orders, :uniq => true
。相反,您现在应该写has_many :products, -> { distinct }, through: :orders
。有关详细信息,请参阅distinct section for has_many: :through relationships on the ActiveRecord Associations documentation。感谢Kurt Mueller在评论中指出这一点。
答案 1 :(得分:40)
请注意,自{Rails 4}起,uniq: true
的有效选项已删除has_many
。
在Rails 4中,您必须提供一个范围来配置此类行为。范围可以通过lambdas提供,如下所示:
has_many :products, -> { uniq }, :through => :orders
导轨指南涵盖了这种以及您可以使用范围过滤关系查询的其他方式,向下滚动到第4.3.3节:
http://guides.rubyonrails.org/association_basics.html#has-many-association-reference
答案 2 :(得分:5)
您可以使用group_by
。例如,我有一个照片库购物车,我希望订购商品按照哪张照片进行分类(每张照片可以多次订购,尺寸不同。)然后,这将返回带有产品(照片)作为关键字的哈希值,并且每次订购时都可以在照片的上下文中列出(或不列出)。使用此技术,您实际上可以输出每个给定产品的订单历史记录。在这种情况下,不确定这对你是否有帮助,但我发现它非常有用。这是代码
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
通过这种方式,您可以避免在仅返回一个产品时出现的问题,因为您始终知道它将返回一个哈希,产品作为密钥和一组订单。
对于您正在尝试做的事情可能有些过分,但除了数量之外,它确实为您提供了一些不错的选择(即订购日期等)。
答案 3 :(得分:4)
在Rails 6上,我可以完美地运行它:
has_many :regions, -> { order(:name).distinct }, through: :sites
我无法获得其他任何答案。
答案 4 :(得分:0)
在 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>]