Cómo mostrar registros únicos de una tiene muchos a través de la relación?


Me pregunto cuál es la mejor manera de mostrar registros únicos de un has_many, a través de la relación en Rails3.

Tengo tres modelos:

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

Digamos que quiero enumerar todos los productos que un cliente ha pedido en su página de presentación.

Es posible que hayan pedido algunos productos varias veces, por lo que estoy usando counter_cache para mostrar en orden de rango descendente, basado en el número de pedidos.

Pero, si han pedido un producto varias veces, necesito asegurarme de que cada producto solo aparece una vez.

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

Funciona cuando hay varios registros de pedidos para un producto, pero genera un error si un producto solo se ha pedido una vez. (ranked es la función de ordenación personalizada definida en otro lugar)

Otra alternativa es:

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

No estoy seguro de que estoy en el enfoque correcto aquí.

¿Alguien más ha abordado esto? ¿A qué problemas te enfrentaste? ¿Dónde puedo encontrar más información sobre la diferencia entre .único! y DISTINCT()?

¿Cuál es la mejor manera de generar una lista de registros únicos a través de una has_many, a través de una relación?

Gracias

Author: Andy Harvey, 2011-05-01

3 answers

¿Ha intentado especificar la opción: uniq en la asociación has_many:

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

De la documentación de Rails :

:uniq

Si es true, los duplicados se omitirán de la colección. Útil en conjunto con: a través.

ACTUALIZACIÓN PARA RAILS 4:

En Rails 4, has_many :products, :through => :orders, :uniq => true está obsoleto. En su lugar, ahora debería escribir has_many :products, -> { distinct }, through: :orders. Vea la sección distinct para has_many:: through relationships en el ActiveRecord Documentación de asociaciones para más información. Gracias a Kurt Mueller por señalar esto en su comentario.

 212
Author: mbreining,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-07 12:04:38

Tenga en cuenta que uniq: true se ha eliminado de las opciones válidas para has_many a partir de Rails 4.

En Rails 4 debe proporcionar un ámbito para configurar este tipo de comportamiento. Los alcances se pueden suministrar a través de lambdas, así:

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

La guía de rails cubre esta y otras formas en las que puedes usar scopes para filtrar las consultas de tu relación, desplázate hacia abajo hasta la sección 4.3.3:

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

 38
Author: Yoshiki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-28 19:47:46

Podrías usar group_by. Por ejemplo, tengo un carrito de compras de la galería de fotos para el que quiero ordenar los artículos por qué foto (cada foto se puede pedir varias veces y en impresiones de diferentes tamaños). Esto entonces devuelve un hash con el producto (foto) como la clave y cada vez que se pidió se puede enumerar en el contexto de la foto (o no). Usando esta técnica, realmente podría generar un historial de pedidos para cada producto dado. No estoy seguro de si eso es útil para usted en este contexto, pero yo me pareció muy útil. Aquí está el código

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

@order_items_by_photo entonces se ve algo como esto:

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

Así que podrías hacer algo como:

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

Luego, cuando obtengas esto en tu vista, simplemente recorre algo como esto:

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

De esta manera evitarás el problema que se ve al devolver solo un producto, ya que siempre sabes que devolverá un hash con un producto como clave y una matriz de tus pedidos.

Podría ser exagerado por lo que estás tratando de hacer, pero le da algunas buenas opciones (es decir, fechas ordenadas, etc.) para trabajar con, además de la cantidad.

 5
Author: Josh Kovach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-01 06:51:18