Как вывести уникальные записи из Много через отношения?


мне интересно, каков наилучший способ отображения уникальных записей из 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)")

Я не уверен, что я на правильном подходе здесь.

кто-нибудь еще занимался этим? С какими проблемами вы столкнулись? Где я могу узнать больше о разнице между ними .уникальный! и отчетливый ()?

каков наилучший способ создания списка уникальных записей через has_many, через отношения?

спасибо

3 94

3 ответа:

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

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

С документация по рельсам:

:uniq

если true, дубликаты будут опущены из коллекции. Полезно в сочетании с :через.

ОБНОВЛЕНИЕ ДЛЯ RAILS 4:

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

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

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

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

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

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

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

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

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