Django में ग्रुप बाय के रूप में क्वेरी कैसे करें?


332

मैं एक मॉडल की क्वेरी करता हूं:

Members.objects.all()

और यह रिटर्न:

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

जो मैं चाहता हूं वह यह है कि group_byमेरे डेटाबेस में किसी क्वेरी को फायर करने का सबसे अच्छा Django तरीका पता हो , जैसे:

Members.objects.all().group_by('designation')

जो निश्चित रूप से काम नहीं करता है। मुझे पता है कि हम कुछ तरकीबें कर सकते हैं django/db/models/query.py, लेकिन मैं सिर्फ यह जानने के लिए उत्सुक हूं कि इसे बिना पैचिंग के कैसे किया जाए।

जवाबों:


483

यदि आप एकत्रीकरण करना चाहते हैं, तो आप ORM के एकत्रीकरण सुविधाओं का उपयोग कर सकते हैं :

from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

इसके परिणामस्वरूप एक क्वेरी होती है

SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

और आउटपुट फॉर्म का होगा

[{'designation': 'Salesman', 'dcount': 2}, 
 {'designation': 'Manager', 'dcount': 2}]

6
@ हैरी: आप इसे चेन कर सकते हैं। कुछ इस तरह:Members.objects.filter(date=some_date).values('designation').annotate(dcount=Count('designation'))
एली

57
मेरे पास एक प्रश्न है, यह क्वेरी केवल पदनाम और dcount लौटा रही है, क्या होगा यदि मैं तालिका के अन्य मूल्यों को भी प्राप्त करना चाहता हूं?
एजे

19
ध्यान दें कि यदि आपकी छंटनी पदनाम के अलावा एक क्षेत्र है, तो यह सॉर्ट को रीसेट किए बिना काम नहीं करेगा। देखें stackoverflow.com/a/1341667/202137
Gidgidonihah

12
@ गिदगनिहोह सच है, उदाहरण को पढ़ना चाहिएMembers.objects.order_by('disignation').values('designation').annotate(dcount=Count('designation'))
bjunix

7
मेरे पास एक प्रश्न है, यह क्वेरी केवल पदनाम और dcount लौटा रही है, क्या होगा यदि मैं तालिका के अन्य मूल्यों को भी प्राप्त करना चाहता हूं?
यन Y

55

एक आसान उपाय, लेकिन कच्चे SQL का उपयोग करने का उचित तरीका नहीं है :

results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

एक अन्य उपाय group_byसंपत्ति का उपयोग करना है:

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

अब आप अपने परिणामों को पुनः प्राप्त करने के लिए परिणाम चर पर पुनरावृति कर सकते हैं। ध्यान दें कि group_byप्रलेखित नहीं है और Django के भविष्य के संस्करण में बदला जा सकता है।

और ... आप का उपयोग क्यों करना चाहते हैं group_by? यदि आप एकत्रीकरण का उपयोग नहीं करते हैं, तो आप order_byएक जैसे परिणाम प्राप्त करने के लिए उपयोग कर सकते हैं ।


क्या आप कृपया मुझे बता सकते हैं कि यह कैसे ऑर्डर_बी का उपयोग कर रहा है ??
बसंती मार्श

2
नमस्ते, यदि आप एकत्रीकरण का उपयोग नहीं कर रहे हैं, तो आप एक आदेश_ का उपयोग करके group_by का अनुकरण कर सकते हैं और उन प्रविष्टियों को समाप्त कर सकते हैं जिनकी आपको आवश्यकता नहीं है। बेशक, यह एक अनुकरण है और बहुत सारे डेटा का उपयोग करते समय केवल उपयोग करने योग्य है। चूंकि वह एकत्रीकरण की बात नहीं करता था, मुझे लगा कि यह एक समाधान हो सकता है।
माइकल

अरे यह महान है - आप कृपया समझा सकते हैं कि कैसे उपयोग execute_sql के लिए यह काम करने के लिए प्रकट नहीं होता है ..
rh0dium

8
ध्यान दें कि यह अब Django 1.9 पर काम नहीं करता है। stackoverflow.com/questions/35558120/…
grokpot

1
यह ओआरएम का उपयोग करने का एक हैक-ईश तरीका है। आपको पुराने वाले मैन्युअल रूप से पास होने वाले नए क्वेरीसेट को तुरंत नहीं करना चाहिए।
इयान किर्कपैट्रिक

32

आप regroupटेम्प्लेट द्वारा समूह में टेम्प्लेट टैग का भी उपयोग कर सकते हैं । डॉक्स से:

cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}

<ul>
    {% for country in country_list %}
        <li>{{ country.grouper }}
            <ul>
            {% for city in country.list %}
                <li>{{ city.name }}: {{ city.population }}</li>
            {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

इस तरह दिखता है:

  • भारत
    • मुंबई: 19,000,000
    • कलकत्ता: 15,000,000
  • अमेरीका
    • न्यूयॉर्क: 20,000,000
    • शिकागो: 7,000,000
  • जापान
    • टोक्यो: 33,000,000

यह भी QuerySetमुझे विश्वास है पर काम करता है।

स्रोत: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

संपादित करें: ध्यान दें कि regroupटैग काम नहीं करता है क्योंकि आप इसकी अपेक्षा करेंगे यदि आपके शब्दकोशों की सूची कुंजी-क्रमबद्ध नहीं है। यह चलने का काम करता है। इसलिए regroupटैग को पास करने से पहले अपनी सूची (या क्वेरी सेट) को ग्रॉपर की कुंजी के आधार पर क्रमबद्ध करें ।


1
यह पूर्ण है! मैंने यह करने के लिए एक सरल तरीके के लिए बहुत कुछ खोजा है। और यह क्वेरीसेट पर भी काम करता है, इस तरह मैंने इसका उपयोग किया।
कारमेनए

1
यह पूरी तरह से गलत है यदि आप डेटाबेस से डेटा के बड़े सेट को पढ़ते हैं और फिर केवल एकत्रित मूल्यों का उपयोग करते हैं।
स्लावोमिर लेनार्ट

@ SławomirLenart यकीन है, यह एक सीधे DB क्वेरी के रूप में कुशल नहीं हो सकता है। लेकिन साधारण उपयोग के मामलों के लिए यह एक अच्छा समाधान हो सकता है
inostia

यदि टेम्पलेट में दिखाया गया है तो यह काम करेगा। लेकिन, JsonResponse या अन्य अप्रत्यक्ष प्रतिक्रिया के लिए। यह समाधान काम नहीं करेगा।
विली सैत्रियो नुग्रोहो

1
@Willysatrionugroho यदि आप इसे एक दृश्य में करना चाहते हैं, उदाहरण के लिए, stackoverflow.com/questions/477820/… आपके लिए काम कर सकता है
Inostia

7

इस स्निपेट में आपको उदाहरण के रूप में कस्टम SQL करने की आवश्यकता है:

कस्टम SQL सबक्वेरी के माध्यम से

या एक कस्टम मैनेजर में जैसा कि ऑनलाइन Django डॉक्स में दिखाया गया है:

अतिरिक्त प्रबंधक विधियाँ जोड़ना


1
राउंड-ट्रिप समाधान की तरह। मैं इसे इस्तेमाल किया होता, अगर मैं उस का कुछ विस्तारित उपयोग होता। लेकिन यहां मुझे सिर्फ प्रति पद के सदस्यों की संख्या की आवश्यकता है।
बसंती मार्श

कोई दिक्कत नहीं है। मैंने 1.1 एकत्रीकरण सुविधाओं का उल्लेख करने के बारे में सोचा था, लेकिन यह अनुमान लगाया कि आप रिलीज़ संस्करण का उपयोग कर रहे थे :)
वान गेल

यह कच्चे प्रश्नों का उपयोग करने के बारे में है, जो कि Django के ORM की कमजोरी को दर्शाता है।
स्लावोमिर लेनार्ट

5

Django प्रश्नों से मुक्त समूह का समर्थन नहीं करता है । मैंने इसे बहुत बुरे तरीके से सीखा। ORM सामान का समर्थन करने के लिए डिज़ाइन नहीं किया गया है जैसे आप कस्टम SQL का उपयोग किए बिना क्या करना चाहते हैं। आप इसके लिए सीमित हैं:

  • RAW sql (यानी MyModel.objects.raw ())
  • cr.execute वाक्य (और परिणाम का एक हाथ से बना पार्स)।
  • .annotate() (वाक्यों के समूह को .annotate () के लिए चाइल्ड मॉडल में एकत्र किया जाता है, उदाहरणों में एग्रीगेटिंग लाइन्स_काउंट = काउंट ('लाइन्स'))।

एक क्वेरी पर आप qsकॉल कर सकते हैं, qs.query.group_by = ['field1', 'field2', ...]लेकिन यह जोखिम भरा है यदि आप नहीं जानते हैं कि आप किस क्वेरी को संपादित कर रहे हैं और इसकी कोई गारंटी नहीं है कि यह क्वेरीसैट ऑब्जेक्ट के इंटर्नल को तोड़ देगा या नहीं। इसके अलावा, यह एक आंतरिक (अनिर्दिष्ट) एपीआई है जिसे आपको सीधे कोड के बिना जोखिम के बिना एक्सेस नहीं करना चाहिए जो कि भविष्य के Django संस्करणों के साथ संगत नहीं है।


वास्तव में आप न केवल मुक्त समूह द्वारा सीमित हैं, इसलिए Django ORM के बजाय SQLAlchemy आज़माएं।
स्लावोमिर लेनार्ट

5

मॉड्यूल है जो आपको Django मॉडल को समूह बनाने की अनुमति देता है और फिर भी परिणाम में एक क्वेरीस के साथ काम करता है: https://github.com/kako-nawao/django-group-by

उदाहरण के लिए:

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

'पुस्तक / books.html'

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

annotate/ aggregateमूलभूत Django प्रश्नों का अंतर संबंधित क्षेत्र की विशेषताओं का उपयोग है, जैसे book.author.last_name

यदि आपको उन उदाहरणों के PKs की आवश्यकता है जो एक साथ समूहीकृत किए गए हैं, तो निम्नलिखित एनोटेशन जोड़ें:

.annotate(pks=ArrayAgg('id'))

ध्यान दें: ArrayAggएक पोस्टग्रेज विशिष्ट कार्य है, जो Django 1.9 के बाद से उपलब्ध है: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg


यह django-group-byvalues विधि का एक विकल्प है । मुझे लगता है कि यह अलग उद्देश्य के लिए है।
राही

1
@Lhi यह मूल्यों का विकल्प नहीं है, बिल्कुल नहीं। valuesएक SQL है, selectजबकि group_byएक SQL है group by(जैसा कि नाम इंगित करता है ...)। क्यों होता है पतन? हम जटिल group_byबयानों को लागू करने के लिए उत्पादन में ऐसे कोड का उपयोग कर रहे हैं ।
रिसादिंह

इसके डॉक में कहा गया है, group_by"ज्यादातर मान पद्धति की तरह व्यवहार करता है, लेकिन एक अंतर के साथ ..." डॉक्टर ने एसक्यूएल का उल्लेख नहीं किया है GROUP BYऔर उपयोग के मामले में यह एसक्यूएल के साथ कुछ भी करने का सुझाव नहीं देता है GROUP BY। जब किसी ने यह स्पष्ट कर दिया है तो मैं डाउन-वोट को वापस खींच लूंगा, लेकिन यह डॉक्टर वास्तव में भ्रामक है।
एलएसआई

के लिए डॉक्टर कोvalues पढ़ने के बाद , मैंने पाया कि मुझे याद आया कि valuesखुद एक ग्रुप बीवाई की तरह काम करता है। यह मेरी गलती है। मुझे लगता है कि itertools.groupbyजब valuesयह अपर्याप्त है तो इस django- समूह द्वारा उपयोग करना सरल है।
LShi

1
डेटाबेस से सबकुछ प्राप्त किए बिना या बिना group byसाधारण valuesकॉल के साथ ऊपर से ऐसा करना असंभव है annotateitertools.groupbyछोटे डेटासेट के लिए आपका सुझाव, लेकिन कई हजारों डेटासेट के लिए नहीं, जिन्हें आप शायद पेज करना चाहते हैं। बेशक, उस बिंदु पर आपको एक विशेष खोज सूचकांक के बारे में सोचना होगा जिसमें तैयार (पहले से समूहीकृत) डेटा, वैसे भी शामिल है।
रिसादिंह

0

दस्तावेज़ का कहना है कि आप क्वेरीसमूह समूह के लिए मूल्यों का उपयोग कर सकते हैं।

class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times, 
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True)) 
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had 
#  visited the interest for 2 times

आप इस कोड का उपयोग करके नाम से सभी पुस्तकें और समूह पा सकते हैं:

Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

आप यहां कुछ चीट शीट देख सकते हैं


-1

अगर मैं गलत नहीं हूँ, तो आप जो भी उपयोग कर सकते हैं, जो भी-क्वेरी-सेट .group_by = [' फ़ील्ड ']


8
यह मामला नहीं है, कम से कम Django 1.6 में: 'QuerySet' ऑब्जेक्ट की कोई विशेषता नहीं है 'group_by'
Facundo Olano

1
एक उचित उपयोग queryset.query.group_by = [...] हो सकता है, लेकिन यह क्वेरी के शब्दार्थ को तोड़ देगा और अपेक्षित रूप से काम नहीं करेगा।
लुइस मूसली

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