관계를 통해 has_many의 고유 레코드를 표시하는 방법은 무엇입니까?


106

Rails3의 관계를 통해 has_many의 고유 레코드를 표시하는 가장 좋은 방법이 무엇인지 궁금합니다.

세 가지 모델이 있습니다.

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를 통해 고유 레코드 목록을 생성하는 가장 좋은 방법은 무엇입니까?

감사

답변:


235

has_many 연관에 : uniq 옵션을 지정하려고 했습니까?

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

에서 레일 문서 :

:uniq

true 인 경우 컬렉션에서 중복 항목이 생략됩니다. : through와 함께 유용합니다.

레일 4 업데이트 :

Rails 4에서는 has_many :products, :through => :orders, :uniq => true더 이상 사용되지 않습니다. 대신 이제 has_many :products, -> { distinct }, through: :orders. 자세한 정보 는 ActiveRecord 연관 문서에서 has_many : : through 관계에 대한 구별 섹션 을 참조하십시오. 그의 의견에서 이것을 지적한 Kurt Mueller에게 감사드립니다.


6
차이점은 모델 또는 컨트롤러에서 중복을 제거하는지 여부가 아니라 연결에서 : uniq 옵션 (내 대답에 표시됨) 또는 SQL DISTINCT stmt (예 : has_many : products, : through => : orders)를 사용하는지 여부입니다. , : select => "DISTINCT products. *). 첫 번째 경우에는 모든 레코드를 가져오고 rails가 중복을 제거합니다. 나중에는 중복되지 않은 레코드 만 db에서 가져 오므로 더 나은 성능을 제공 할 수 있습니다. 큰 결과 세트가있는 경우
mbreining

68
Rails 4에서는 has_many :products, :through => :orders, :uniq => true더 이상 사용되지 않습니다. 대신 이제 has_many :products, -> { uniq }, through: :orders.
Kurt Mueller 2014 년

8
이 의미에서-> {uniq}는-> {distinct} apidock.com/rails/v4.1.8/ActiveRecord/QueryMethods/uniq 의 별칭 일뿐입니다. 루비가 아닌 SQL에서 발생합니다
engineerDave

5
DISTINCTORDER BY절 과 충돌하는 경우 항상 사용할 수 있습니다.has_many :products, -> { unscope(:order).distinct }, through: :orders
fagiani

1
@fagiani에게 감사하고 모델에 json 열이 있고 psql을 사용하면 더 복잡해지며 다음과 같은 작업을 수행해야합니다. has_many :subscribed_locations, -> { unscope(:order).select("DISTINCT ON (locations.id) locations.*") },through: :people_publication_subscription_locations, class_name: 'Location', source: :location 당신이 얻을 그렇지 않으면rails ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR: could not identify an equality operator for type json
ryan2johnson9

43

참고 uniq: true를 위해 유효한 옵션에서 제거 된 has_many레일 (4)로.

Rails 4에서는 이런 종류의 동작을 구성하기 위해 범위를 제공해야합니다. 범위는 다음과 같이 람다를 통해 제공 될 수 있습니다.

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

레일 가이드에서는 범위를 사용하여 관계의 쿼리를 필터링 할 수있는이 방법과 기타 방법을 다룹니다. 섹션 4.3.3으로 스크롤합니다.

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


4
레일 5.1, 나는 점점 유지 undefined method 'except' for #<Array...하고 내가 사용 때문이었다 .uniq대신.distinct
stevenspiel

5

당신은 사용할 수 있습니다 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

이렇게하면 하나의 제품 만 반환 할 때 나타나는 문제를 방지 할 수 있습니다. 제품이 키로 포함 된 해시와 주문 배열이 반환된다는 것을 항상 알고 있기 때문입니다.

그것은 당신이하려는 일에 과잉 일 수 있지만, 수량 외에도 작업 할 수있는 좋은 옵션 (예 : 주문한 날짜 등)을 제공합니다.


자세한 답변에 감사드립니다. 이것은 내가 필요로하는 것보다 조금 더 많을 수 있지만 그럼에도 불구하고 배우는 것은 흥미 롭습니다 (실제로 나는 이것을 앱의 다른 곳에서 사용할 수 있습니다). 이 페이지에 언급 된 다양한 접근 방식의 성능에 대해 어떻게 생각하십니까?
Andy Harvey

2

Rails 6에서 완벽하게 작동하도록했습니다.

  has_many :regions, -> { order(:name).distinct }, through: :sites

다른 답을 얻지 못했습니다.


5.2+ 레일에서 동일
Glenn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.