How to display unique records from a has_many through relationship?
I'm wondering what is the best way to display unique records from a has_many, through relationship in Rails3.
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.
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()?
What is the best way to generate a list of unique records through a has_many, through relationship?
Thanks
Solution 1:
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.
Solution 2:
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