संबंध के माध्यम से 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

कहते हैं कि मैं उन सभी उत्पादों को सूचीबद्ध करना चाहता हूं जो एक ग्राहक ने अपने शो पेज पर ऑर्डर किया है।

उन्होंने कई उत्पादों को कई बार ऑर्डर किया होगा, इसलिए मैं आदेशों की संख्या के आधार पर अवरोही क्रम में प्रदर्शित करने के लिए काउंटर_ कैश का उपयोग कर रहा हूं।

लेकिन, यदि उन्होंने कई बार किसी उत्पाद का ऑर्डर दिया है, तो मुझे यह सुनिश्चित करने की आवश्यकता है कि प्रत्येक उत्पाद केवल एक बार सूचीबद्ध हो।

@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

यदि सही है, तो डुप्लिकेट को संग्रह से हटा दिया जाएगा। के साथ संयोजन के रूप में उपयोगी: के माध्यम से।

नाखूनों के लिए अद्यतन 4:

रेल 4 में, has_many :products, :through => :orders, :uniq => trueपदावनत है। इसके बजाय, आपको अब लिखना चाहिए has_many :products, -> { distinct }, through: :ordersHas_many के लिए अलग अनुभाग देखें : अधिक जानकारी के लिए ActiveRecord संघों प्रलेखन पर संबंधों के माध्यम से । अपनी टिप्पणी में इस ओर इशारा करने के लिए कर्ट मुलर का धन्यवाद।


6
अंतर इतना नहीं है कि आप मॉडल या नियंत्रक में डुप्लिकेट को हटाते हैं, बल्कि आप इसका उपयोग करते हैं: अपने संघ पर uniq विकल्प (जैसा कि मेरे उत्तर में दिखाया गया है) या SQL DISTINCT stmt (जैसे has_many: उत्पादों: = => के माध्यम से: आदेश :: select => "DISTINCT उत्पाद। *)। पहले मामले में, सभी रिकॉर्ड प्राप्त किए जाते हैं और रेल आपके लिए डुप्लिकेट को हटा देती है। बाद के मामले में, केवल गैर-डुप्लिकेट रिकॉर्ड डीबी से प्राप्त किए जाते हैं ताकि यह बेहतर प्रदर्शन हो सके। यदि आप किसी बड़े परिणाम सेट है।
mbreining

68
रेल 4 में, has_many :products, :through => :orders, :uniq => trueपदावनत है। इसके बजाय, आपको अब लिखना चाहिए has_many :products, -> { uniq }, through: :orders
कर्ट मुलर

8
ध्यान दें कि -> {uniq} इस अर्थ में बस के लिए एक उपनाम है -> {अलग} apidock.com/rails/v4.1.8/ActiveRecord/QueryMethods/uniq यह एसक्यूएल में गहरे लाल रंग का नहीं होता है
engineerDave

5
यदि आप के साथ टकराव हो जाता है, DISTINCTऔर ORDER BYआप हमेशा का उपयोग कर सकते हैंhas_many :products, -> { unscope(:order).distinct }, through: :orders
फाग्नी

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

ध्यान दें कि नियम 4 के uniq: trueलिए वैध विकल्पों में से हटा दिया गया है has_many

रेल 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

इस तरह से आप केवल एक उत्पाद को वापस करते समय देखे जाने वाले मुद्दे से बचते हैं, क्योंकि आपको हमेशा पता होता है कि यह कुंजी और आपके आदेशों की एक सरणी के रूप में एक उत्पाद के साथ एक हैश लौटाएगा।

यह आप क्या करने की कोशिश कर रहे हैं के लिए overkill हो सकता है, लेकिन यह आपको मात्रा के अलावा काम करने के लिए कुछ अच्छे विकल्प (यानी तारीखों, आदि का आदेश) देता है।


विस्तृत उत्तर के लिए धन्यवाद। यह मेरी आवश्यकता से थोड़ा अधिक हो सकता है, लेकिन फिर भी नॉनवेज से सीखना दिलचस्प होगा (वास्तव में मैं इसे ऐप में कहीं और उपयोग कर सकता हूं)। इस पृष्ठ पर उल्लिखित विभिन्न दृष्टिकोणों के लिए प्रदर्शन पर आपके विचार क्या हैं?
एंडी हार्वे

2

रेल 6 पर मुझे यह पूरी तरह से काम करने के लिए मिला:

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

मुझे काम करने के लिए कोई अन्य उत्तर नहीं मिला।


उसी रेल पर 5.2+
ग्लेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.