121 votes

Comment afficher des enregistrements uniques à partir d'une relation has_many?

Je me demande quelle est la meilleure façon d'afficher les enregistrements uniques à partir d'un has_many, la relation d'aide dans Rails3.

J'ai trois modèles:

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

Disons que je veux la liste de tous les produits d'un client a commandé sur leur page.

Ils peuvent avoir commandé des produits à plusieurs reprises, donc je suis en utilisant counter_cache pour afficher en ordre décroissant, en fonction du nombre de commandes.

Mais, s'ils ont commandé un produit à plusieurs reprises, j'ai besoin de s'assurer que chaque produit est seulement une fois que.

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

fonctionne lorsqu'il y a plusieurs enregistrements de commande pour un produit, mais génère une erreur si un produit a été commandé une fois. (classement est fonction de tri personnalisé défini par ailleurs)

Une autre alternative est la suivante:

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

Je ne suis pas convaincu que je suis sur la bonne approche ici.

Quelqu'un d'autre a abordé ce problème? Quelles questions avez-vous contre? Où puis-je trouver plus d'information à propos de la différence entre les deux .unique! et DISTINCT()?

Quelle est la meilleure façon de générer une liste de dossiers uniques par le biais d'un has_many, la relation d'aide?

Merci

271voto

mbreining Points 3981

Avez-vous essayé de préciser le :uniq option sur la has_many de l'association:

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

De les Rails de la documentation:

:uniq

Si la valeur est true, les doublons seront omis de la collection. Utile en conjonction avec :par.

51voto

Yoshiki Points 41

Notez que uniq: true a été supprimé à partir des options valides pour has_many de Rails 4.

Dans les Rails 4 vous devez fournir un champ d'application pour configurer ce genre de comportement. Les étendues peuvent être fournis par le biais des lambdas, comme suit:

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

Les rails de guide traite de cela et d'autres façons que vous pouvez utiliser des étendues de filtrer votre relation avec vos interrogations, faites défiler jusqu'à la section 4.3.3:

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

5voto

Josh Kovach Points 2759

Vous pouvez utiliser group_by. Par exemple, j'ai une galerie photo panier pour laquelle je veux commander des articles triés par photo (chaque photo peut être commandé à plusieurs reprises et dans différentes impressions). Ensuite, cela renvoie une valeur de hachage avec le produit (photo) comme clé et à chaque fois il a été ordonné peuvent être répertoriés dans le contexte de la photo (ou pas). En utilisant cette technique, vous pouvez effectivement la sortie d'une commande historique pour chaque produit. Vous ne savez pas si c'est utile pour vous dans ce contexte, mais je l'ai trouvé très utile. Voici le code

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

@order_items_by_photo alors ressemble à quelque chose comme ceci:

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

Donc, vous pourriez faire quelque chose comme:

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

Puis, quand vous obtenez ce à votre avis, juste une boucle par quelque chose comme ceci:

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

De cette façon, vous évitez la question vu lors du retour d'un seul produit, car vous savez toujours qu'il sera de retour une valeur de hachage avec un produit comme clé et un tableau de vos commandes.

Il serait peut-être exagéré pour ce que vous êtes en train de faire, mais il ne vous donner quelques options sympathiques (c'est à dire les dates de l'ordre, etc.) travailler avec en plus de la quantité.

-1voto

Prateek Mittal Points 29

Essayez Rails uniq_by.

@products = @user.uniq_by(:products)

Plus de détails http://www.brownwebdesign.com/blog/2012/12/rails-uniq_by-unique-results-from-activerecord-relations/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X