Have you tried to specify the :uniq option on the has_many association:
has_many :products, :through => :orders, :uniq => true
From the Rails documentation:
:uniq
If true, duplicates will be omitted from the collection. Useful in conjunction with :through.
UPDATE FOR RAILS 4:
In Rails 4, has_many :products, :through => :orders, :uniq => true
is 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 documentation for more information. Thanks to Kurt Mueller for pointing this out in his comment.
Note that uniq: true
has been removed from the valid options for has_many
as of Rails 4.
In Rails 4 you have to supply a scope to configure this kind of behavior. Scopes can be supplied through lambdas, like so:
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:
http://guides.rubyonrails.org/association_basics.html#has-many-association-reference
On Rails 6 I got this to work perfectly:
has_many :regions, -> { order(:name).distinct }, through: :sites
I couldn't get any of the other answers to work.
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
then looks something like this:
=> {#<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.
in rails6 use -> { distinct }
in scope it will work
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>]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With