Как отобразить уникальные записи из отношения has_many через? - PullRequest
98 голосов
/ 01 мая 2011

Мне интересно, как лучше всего отобразить уникальные записи из has_many через отношения в Rails3.

У меня есть три модели:

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, через отношения?

Спасибо

Ответы [ 3 ]

220 голосов
/ 01 мая 2011

Вы пытались указать параметр: uniq в ассоциации has_many:

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

С Документация по Rails :

:uniq

Если true, дубликаты будут исключены из коллекции. Полезно в сочетании с: through.

ОБНОВЛЕНИЕ РЕЛЬСОВ 4:

В Rails 4 has_many :products, :through => :orders, :uniq => true устарело. Вместо этого вы должны написать has_many :products, -> { distinct }, through: :orders. См. Отдельный раздел для has_many:: through отношений в документации ассоциаций ActiveRecord для получения дополнительной информации. Спасибо Курту Мюллеру за то, что он указал на это в своем комментарии.

40 голосов
/ 28 августа 2014

Обратите внимание, что uniq: true был удален из допустимых опций для has_many с Rails 4.

В Rails 4 вы должны указать область действия для настройки такого поведения. Области могут быть поставлены через лямбды, например так:

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

Руководство по рельсам описывает этот и другие способы использования областей для фильтрации запросов вашего отношения, прокрутите вниз до раздела 4.3.3:

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

5 голосов
/ 01 мая 2011

Вы можете использовать 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

Таким образом вы избежите проблемы, возникающей при возврате только одного продукта, поскольку вы всегда знаете, что он вернет хеш с ключом продукта и массивом ваших заказов.

Это может быть излишним для того, что вы пытаетесь сделать, но оно дает вам несколько хороших вариантов (например, даты заказа и т. Д.) Для работы в дополнение к количеству.

...