Rails3のリレーションシップを使用して、has_manyから一意のレコードを表示する最善の方法は何だろうと思っています。
3つのモデルがあります。
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、リレーションシップを通じて一意のレコードのリストを生成する最良の方法は何ですか?
ありがとう
Has_manyアソシエーションで:uniqオプションを指定しようとしましたか?
has_many :products, :through => :orders, :uniq => true
から Rails documentation :
:uniq
Trueの場合、重複はコレクションから省略されます。 :throughと組み合わせて使用すると便利です。
UPDATE FOR Rails 4:
In Rails 4、has_many :products, :through => :orders, :uniq => true
は非推奨です。代わりに、has_many :products, -> { distinct }, through: :orders
。詳細については、 ActiveRecord Associationsドキュメントのhas_many::through関係の個別セクション を参照してください。彼のコメントでこれを指摘してくれたカート・ミューラーに感謝します。
Rails 4の時点で、uniq: true
の有効なオプションからhas_many
が削除されていることに注意してください。
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
これにより、1つの製品のみを返すときに見られる問題を回避できます。これは、製品がキーおよび注文の配列であるハッシュを返すことが常にわかっているためです。
あなたがしようとしていることはやりすぎかもしれませんが、数量に加えて作業するニースのオプション(つまり、注文日など)を提供します。