Django 2.1

Aggregation




django

Aggregation

Django के डेटाबेस-एब्स्ट्रैक्शन API पर विषय मार्गदर्शिका ने उस तरीके का वर्णन किया है जिससे आप व्यक्तिगत वस्तुओं को बनाने, पुनर्प्राप्त करने, अपडेट करने और हटाने के लिए Django क्वेरी का उपयोग कर सकते हैं। हालांकि, कभी-कभी आपको उन मूल्यों को पुनः प्राप्त करने की आवश्यकता होगी जो वस्तुओं के संग्रह को संक्षेप या एकत्र करके प्राप्त होते हैं। यह विषय मार्गदर्शिका उन तरीकों का वर्णन करती है जो समग्र मान उत्पन्न कर सकते हैं और Django प्रश्नों का उपयोग करके वापस आ सकते हैं।

इस गाइड के दौरान, हम निम्नलिखित मॉडलों का उल्लेख करेंगे। इन मॉडलों का उपयोग ऑनलाइन बुकस्टोर की श्रृंखला के लिए इन्वेंट्री को ट्रैक करने के लिए किया जाता है:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Publisher(models.Model):
    name = models.CharField(max_length=300)
    num_awards = models.IntegerField()

class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    pubdate = models.DateField()

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)
    registered_users = models.PositiveIntegerField()

प्रवंचक पत्रक

जल्दी में? ऊपर दिए गए मॉडल को मानते हुए, सामान्य सामान्य प्रश्न कैसे करें:

# Total number of books.
>>> Book.objects.count()
2452

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73

# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max('price', output_field=FloatField()) - Avg('price'))
{'price_diff': 46.85}

# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.

# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73

# Each publisher, with a separate count of books with a rating above and below 5
>>> from django.db.models import Q
>>> above_5 = Count('book', filter=Q(book__rating__gt=5))
>>> below_5 = Count('book', filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12

# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323

एक QuerySet पर कुल एकत्रित करना

Django समुच्चय उत्पन्न करने के लिए दो तरीके प्रदान करता है। पहला तरीका पूरे QuerySet पर सारांश मान उत्पन्न QuerySet । उदाहरण के लिए, मान लें कि आप बिक्री के लिए उपलब्ध सभी पुस्तकों की औसत कीमत की गणना करना चाहते हैं। Django की क्वेरी सिंटैक्स सभी पुस्तकों के सेट का वर्णन करने के लिए एक साधन प्रदान करता है:

>>> Book.objects.all()

हमें इस QuerySet से संबंधित वस्तुओं पर सारांश मानों की गणना करने का एक तरीका है। यह QuerySet पर aggregate() खंड जोड़कर किया जाता है:

>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

all() इस उदाहरण में बेमानी है, इसलिए इसे सरल बनाया जा सकता है:

>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate() क्लॉज का तर्क उस एग्रीगेट वैल्यू का वर्णन करता है जिसे हम गणना करना चाहते हैं - इस मामले में, Book मॉडल पर price क्षेत्र का औसत। कुल उपलब्ध कार्यों की एक सूची को क्वेरीसेट संदर्भ में पाया जा सकता है।

aggregate() एक QuerySet लिए एक टर्मिनल क्लॉज है, जिसे जब आमंत्रित किया जाता है, तो नाम-मूल्य जोड़े का एक शब्दकोश देता है। नाम कुल मूल्य के लिए एक पहचानकर्ता है; मान अभिकलित समुच्चय है। नाम स्वचालित रूप से फ़ील्ड और कुल फ़ंक्शन के नाम से उत्पन्न होता है। यदि आप मैन्युअल रूप से कुल मान के लिए एक नाम निर्दिष्ट करना चाहते हैं, तो आप समुच्चय खंड निर्दिष्ट करते समय उस नाम को प्रदान करके ऐसा कर सकते हैं:

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

यदि आप एक से अधिक समुच्चय उत्पन्न करना चाहते हैं, तो आप aggregate() खंड में एक और तर्क जोड़ते हैं। इसलिए, यदि हम सभी पुस्तकों की अधिकतम और न्यूनतम कीमत जानना चाहते हैं, तो हम क्वेरी जारी करेंगे:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

QuerySet में प्रत्येक आइटम के लिए समुच्चय QuerySet

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

annotate() क्लॉज का उपयोग करके प्रति-वस्तु सारांश उत्पन्न किया जा सकता है। जब एक annotate() क्लॉज निर्दिष्ट किया जाता है, तो QuerySet में प्रत्येक ऑब्जेक्ट को निर्दिष्ट मानों के साथ एनोटेट किया जाएगा।

इन एनोटेशन के लिए सिंटैक्स aggregate() क्लॉज के समान उपयोग किया जाता है। annotate() करने के लिए प्रत्येक तर्क annotate() एक कुल का वर्णन करता है जिसे गणना करना है। उदाहरण के लिए, लेखकों की संख्या के साथ पुस्तकों को एनोटेट करना:

# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

aggregate() , एनोटेशन के लिए नाम स्वचालित रूप से एग्रीगेट फ़ंक्शन के नाम और फील्ड के एग्रीगेट होने के नाम से लिया गया है। जब आप एनोटेशन निर्दिष्ट करते हैं, तो आप एक उपनाम प्रदान करके इस डिफ़ॉल्ट नाम को ओवरराइड कर सकते हैं:

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

aggregate() विपरीत, annotate() टर्मिनल क्लॉज नहीं है। annotate() क्लॉज़ का आउटपुट एक QuerySet ; इस QuerySet को किसी भी अन्य QuerySet ऑपरेशन का उपयोग करके संशोधित किया जा सकता है, जिसमें filter() , order_by() , या यहां तक ​​कि अतिरिक्त कॉल को annotate()

कई एकत्रीकरण का संयोजन

annotate() साथ कई एग्रीगेशन को annotate() से गलत परिणाम मिलेंगे क्योंकि सब-वे के बजाय जॉइन का उपयोग किया जाता है:

>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count('authors'), Count('store'))
>>> q[0].authors__count
6
>>> q[0].store__count
6

अधिकांश समुच्चय के लिए, इस समस्या से बचने का कोई तरीका नहीं है, हालाँकि, Count समुच्चय का एक distinct पैरामीटर है जो मदद कर सकता है:

>>> q = Book.objects.annotate(Count('authors', distinct=True), Count('store', distinct=True))
>>> q[0].authors__count
2
>>> q[0].store__count
3

यदि संदेह है, तो SQL क्वेरी का निरीक्षण करें!

यह समझने के लिए कि आपकी क्वेरी में क्या होता है, अपने QuerySet की query प्रॉपर्टी का निरीक्षण करने पर विचार करें।

जुड़ता है और एकत्र करता है

अब तक, हमने उन क्षेत्रों पर समुच्चय को लागू किया है जो कि मॉडल से संबंधित हैं। हालाँकि, कभी-कभी आप जो मूल्य एकत्र करना चाहते हैं, वह उस मॉडल का होगा जो उस मॉडल से संबंधित है जिसे आप क्वेरी कर रहे हैं।

जब फ़ील्ड को एक कुल फ़ंक्शन में संयोजित करने के लिए निर्दिष्ट किया जाता है, तो Django आपको उसी डबल अंडरस्कोर नोटेशन का उपयोग करने की अनुमति देगा जो फ़िल्टर में संबंधित फ़ील्ड का संदर्भ देते समय उपयोग किया जाता है। Django तब संबंधित मूल्य को पुनः प्राप्त करने और एकत्र करने के लिए आवश्यक किसी भी टेबल जॉइन को संभाल लेगा।

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

>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

यह Django को Store मॉडल को पुनः प्राप्त करने के लिए कहता है, Book मॉडल के साथ (कई-से-कई संबंधों के माध्यम से), और न्यूनतम और अधिकतम मूल्य का उत्पादन करने के लिए पुस्तक मॉडल के मूल्य क्षेत्र पर एकत्र Book

समान नियम aggregate() खंड पर लागू होते हैं। यदि आप किसी भी पुस्तक की न्यूनतम और उच्चतम कीमत जानना चाहते हैं, जो किसी भी स्टोर में बिक्री के लिए उपलब्ध है, तो आप कुल का उपयोग कर सकते हैं:

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

जंजीरों में शामिल होना जितना आवश्यक हो उतना गहरा हो सकता है। उदाहरण के लिए, बिक्री के लिए उपलब्ध किसी भी पुस्तक के सबसे कम उम्र के लेखक को निकालने के लिए, आप प्रश्न जारी कर सकते हैं:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

पीछे के रिश्तों का पालन

एक तरह से लुकअप के समान, जो आपके द्वारा क्वेरी किए जा रहे मॉडल से संबंधित मॉडल या मॉडल के क्षेत्रों पर संबंधों , एकत्रीकरण और एनोटेशन को फैलाते हैं , जिसमें "रिवर्स" रिश्तों को शामिल करना शामिल हो सकता है। संबंधित मॉडल और डबल-अंडरस्कोर के लोअरकेस नाम का उपयोग यहां भी किया जाता है।

उदाहरण के लिए, हम सभी प्रकाशकों से पूछ सकते हैं, उनके संबंधित कुल बुक स्टॉक काउंटरों के साथ एनोटेट किया गया है (ध्यान दें कि Publisher को निर्दिष्ट करने के लिए हम 'book' का उपयोग कैसे करते हैं -> Book रिवर्स विदेशी कुंजी हॉप):

>>> from django.db.models import Avg, Count, Min, Sum
>>> Publisher.objects.annotate(Count('book'))

(परिणामस्वरूप QuerySet प्रत्येक Publisher पास book__count नामक एक अतिरिक्त विशेषता book__count ।)

हम हर प्रकाशक द्वारा प्रबंधित उन लोगों में से सबसे पुरानी पुस्तक के लिए भी पूछ सकते हैं:

>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

(परिणामी शब्दकोष में एक कुंजी होगी, जिसे 'oldest_pubdate' कहा जाएगा। यदि ऐसा कोई अन्य निर्दिष्ट नहीं किया गया था, तो यह लंबे समय तक 'book__pubdate__min' ।)

यह सिर्फ विदेशी कुंजी पर लागू नहीं होता है। यह कई-कई संबंधों के साथ भी काम करता है। उदाहरण के लिए, हम प्रत्येक लेखक के लिए उन सभी पुस्तकों की कुल संख्या के साथ पूछ सकते हैं, जो लेखक की सभी पुस्तकों पर विचार कर रही हैं (सह-) लेखक हैं (ध्यान दें कि Author को निर्दिष्ट करने के लिए हम 'book' का उपयोग कैसे करते हैं -> Book कई-से-कई रिवर्स Book हॉप):

>>> Author.objects.annotate(total_pages=Sum('book__pages'))

(जिसके परिणामस्वरूप QuerySet प्रत्येक Author पास total_pages नामक एक अतिरिक्त विशेषता total_pages । यदि ऐसा कोई अन्य उपनाम निर्दिष्ट नहीं किया गया था, तो यह लंबे समय तक book__pages__sum ।)

या लेखक द्वारा लिखी गई सभी पुस्तकों की औसत रेटिंग के लिए पूछें: हमारे पास फाइल है:

>>> Author.objects.aggregate(average_rating=Avg('book__rating'))

(परिणामी शब्दकोष में एक कुंजी होगी, जिसे 'average_rating' कहा जाता है। यदि ऐसा कोई अन्य निर्दिष्ट नहीं किया गया था, तो यह बहुत लंबा 'book__rating__avg' ।)

एकत्रीकरण और अन्य QuerySet खंड

filter() और exclude()

एग्रीगेट फिल्टर में भी भाग ले सकते हैं। सामान्य मॉडल फ़ील्ड्स पर लागू किसी भी filter() (या exclude() ) को एकत्रीकरण के लिए मानी जाने वाली वस्तुओं पर दबाव बनाने का प्रभाव पड़ेगा।

जब एक annotate() क्लॉज के साथ उपयोग किया जाता है, तो एक फिल्टर में उन वस्तुओं को कसने का प्रभाव होता है जिनके लिए एनोटेशन की गणना की जाती है। उदाहरण के लिए, आप उन सभी पुस्तकों की एक एनोटेट सूची तैयार कर सकते हैं जिनके पास शीर्षक का उपयोग करके "Django" के साथ शुरू होता है:

>>> from django.db.models import Avg, Count
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

जब एक aggregate() क्लॉज के साथ उपयोग किया जाता है, तो एक फिल्टर में उन वस्तुओं को विवश करने का प्रभाव होता है, जिन पर कुल की गणना की जाती है। उदाहरण के लिए, आप क्वेरी का उपयोग करके "Django" से शुरू होने वाले शीर्षक के साथ सभी पुस्तकों का औसत मूल्य उत्पन्न कर सकते हैं:

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

एनोटेशन पर फ़िल्टर करना

एनोटेट मूल्यों को भी फ़िल्टर किया जा सकता है। एनोटेशन के लिए उपनाम का उपयोग filter() और exclude() खंडों में उसी तरह किया जा सकता है जैसे किसी अन्य मॉडल क्षेत्र में किया जाता है।

उदाहरण के लिए, उन पुस्तकों की सूची तैयार करने के लिए जिनके पास एक से अधिक लेखक हैं, आप क्वेरी जारी कर सकते हैं:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

यह क्वेरी एक एनोटेट परिणाम सेट उत्पन्न करता है, और फिर उस एनोटेशन के आधार पर एक फ़िल्टर बनाता है।

यदि आपको दो अलग-अलग फ़िल्टर के साथ दो एनोटेशन की आवश्यकता है तो आप किसी भी कुल के साथ filter तर्क का उपयोग कर सकते हैं। उदाहरण के लिए, उच्च श्रेणी की पुस्तकों की गिनती के साथ लेखकों की सूची तैयार करने के लिए:

>>> highly_rated = Count('books', filter=Q(books__rating__gte=7))
>>> Author.objects.annotate(num_books=Count('books'), highly_rated_books=highly_rated)

परिणाम सेट में प्रत्येक Author पास num_books और highly_rated_books विशेषताएँ highly_rated_books

filter और QuerySet.filter() बीच चयन

एकल एनोटेशन या एकत्रीकरण के साथ filter तर्क का उपयोग करने से बचें। पंक्तियों को बाहर करने के लिए QuerySet.filter() का उपयोग करना अधिक कुशल है। एकत्रीकरण filter तर्क केवल तभी उपयोगी होता है जब विभिन्न सशर्तों के साथ एक ही संबंध में दो या अधिक एकत्रीकरण का उपयोग किया जाता है।

Django 2.0 में बदला:

filter तर्क को समुच्चय में जोड़ा गया था।

annotate() आदेश annotate() और filter() खंड

एक जटिल क्वेरी विकसित करते समय जिसमें annotate() और filter() क्लॉज़ दोनों शामिल होते हैं, उस आदेश पर विशेष ध्यान दें जिसमें क्लॉज़ QuerySet लागू होते हैं।

जब किसी annotate() क्लॉज को क्वेरी में लागू किया जाता है, तो एनोटेशन को क्वेरी की स्थिति पर उस बिंदु तक गणना की जाती है, जहां एनोटेशन का अनुरोध किया जाता है। इसका व्यावहारिक निहितार्थ यह है कि filter() और annotate() कम्यूटेटिव ऑपरेशन नहीं हैं।

दिया हुआ:

  • प्रकाशक ए की रेटिंग 4 और 5 के साथ दो पुस्तकें हैं।
  • प्रकाशक B की रेटिंग 1 और 4 वाली दो पुस्तकें हैं।
  • प्रकाशक सी की रेटिंग 1 के साथ एक किताब है।

Count एग्रीगेट के साथ एक उदाहरण यहां दिया गया है:

>>> a, b = Publisher.objects.annotate(num_books=Count('book', distinct=True)).filter(book__rating__gt=3.0)
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 2)

>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 1)

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

पहली क्वेरी में, एनोटेशन फ़िल्टर से पहले निकलता है, इसलिए फ़िल्टर का एनोटेशन पर कोई प्रभाव नहीं पड़ता है। distinct=True क्वेरी बग से बचने के लिए distinct=True होना आवश्यक है।

दूसरी क्वेरी उन पुस्तकों की संख्या को गिनाती है जिनकी रेटिंग प्रत्येक प्रकाशक के लिए 3.0 से अधिक है। फ़िल्टर एनोटेशन से पहले होता है, इसलिए फ़िल्टर एनोटेशन की गणना करते समय विचार की जाने वाली वस्तुओं को संकुचित करता है।

यहाँ Avg कुल के साथ एक और उदाहरण दिया गया है:

>>> a, b = Publisher.objects.annotate(avg_rating=Avg('book__rating')).filter(book__rating__gt=3.0)
>>> a, a.avg_rating
(<Publisher: A>, 4.5)  # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 2.5)  # (1+4)/2

>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(avg_rating=Avg('book__rating'))
>>> a, a.avg_rating
(<Publisher: A>, 4.5)  # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 4.0)  # 4/1 (book with rating 1 excluded)

पहली क्वेरी प्रकाशक के लिए सभी प्रकाशक की पुस्तकों की औसत रेटिंग के लिए पूछती है जिनकी रेटिंग 3.0 से अधिक रेटिंग वाली कम से कम एक पुस्तक है। दूसरी क्वेरी किसी प्रकाशक की पुस्तक की रेटिंग का औसत केवल उन रेटिंगों के लिए पूछती है जो 3.0 से अधिक है।

यह संदेह करना मुश्किल है कि ORM जटिल क्वेरी को SQL प्रश्नों में कैसे अनुवाद करेगा, ताकि संदेह होने पर, SQL के साथ str(queryset.query) का निरीक्षण करें और बहुत सारे परीक्षण लिखें।

order_by()

आदेशों के लिए एनोटेशन का उपयोग आधार के रूप में किया जा सकता है। जब आप एक order_by() क्लॉज को परिभाषित करते हैं, तो आपके द्वारा प्रदान किए गए एग्रीगेट्स क्वेरी में annotate() क्लॉज के हिस्से के रूप में परिभाषित किसी भी अन्य उपनाम को संदर्भित कर सकते हैं।

उदाहरण के लिए, किताबों में योगदान देने वाले लेखकों की संख्या के QuerySet से किताबों की क्वेरी करने के लिए, आप निम्नलिखित प्रश्न का उपयोग कर सकते हैं:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

values()

आमतौर पर, एनोटेशन एक प्रति-ऑब्जेक्ट के आधार पर उत्पन्न होते हैं - एक एनोटेट QuerySet मूल QuerySet में प्रत्येक ऑब्जेक्ट के लिए एक परिणाम QuerySet । हालाँकि, जब कोई values() क्लॉज़ का उपयोग परिणाम सेट में दिए गए कॉलम को बाधित करने के लिए किया जाता है, तो एनोटेशन का मूल्यांकन करने का तरीका थोड़ा अलग होता है। मूल QuerySet में प्रत्येक परिणाम के लिए एक एनोटेट परिणाम वापस करने के बजाय, मूल परिणाम values() खंड में निर्दिष्ट फ़ील्ड के अद्वितीय संयोजनों के अनुसार समूहीकृत किए जाते हैं। एक एनोटेशन तब प्रत्येक अद्वितीय समूह के लिए प्रदान किया जाता है; एनोटेशन की गणना समूह के सभी सदस्यों पर की जाती है।

उदाहरण के लिए, एक लेखक क्वेरी पर विचार करें जो प्रत्येक लेखक द्वारा लिखित पुस्तकों की औसत रेटिंग का पता लगाने का प्रयास करती है:

>>> Author.objects.annotate(average_rating=Avg('book__rating'))

यह डेटाबेस में प्रत्येक लेखक के लिए एक परिणाम लौटाएगा, उनकी औसत पुस्तक रेटिंग के साथ एनोटेट किया जाएगा।

हालाँकि, यदि आप values() उपयोग करते हैं तो परिणाम थोड़ा अलग होगा values() खंड:

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

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

annotate() आदेश annotate() और values() खंड

filter() क्लॉज़ के साथ, जिस क्रम में annotate() और values() क्लॉस लागू होते हैं, वह महत्वपूर्ण है। यदि values() क्लॉज annotate() से पहले हैं, तो एनोटेशन की गणना values() क्लॉज द्वारा वर्णित समूहीकरण का उपयोग करके की जाएगी।

हालाँकि, यदि annotate() क्लॉज values() annotate() क्लॉज़ से पहले होता है, तो एनोटेशन पूरे क्वेरी सेट पर उत्पन्न होंगे। इस स्थिति में, values() क्लॉज़ केवल उन फ़ील्ड्स को रोकता है जो आउटपुट पर उत्पन्न होते हैं।

उदाहरण के लिए, यदि हम अपने पिछले उदाहरण से values() के क्रम values() और annotate() खंड को उलट देते हैं:

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

यह अब प्रत्येक लेखक के लिए एक अनूठा परिणाम देगा; हालाँकि, आउटपुट डेटा में केवल लेखक का नाम और average_rating एनोटेशन वापस किया जाएगा।

आपको यह भी ध्यान देना चाहिए कि average_rating को लौटाए जाने वाले मूल्यों की सूची में स्पष्ट रूप से शामिल किया गया है। values() और annotate() खंड के आदेश के कारण इसकी आवश्यकता है।

यदि values() क्लॉज annotate() values() क्लॉज से पहले होता है, तो कोई भी एनोटेशन स्वचालित रूप से परिणाम सेट में जोड़ दिया जाएगा। हालाँकि, यदि annotate() क्लॉज़ के बाद values() क्लॉज लागू होता है, तो आपको एग्रीगेट कॉलम को स्पष्ट रूप से शामिल करना होगा।

डिफ़ॉल्ट ऑर्डर या order_by() साथ सहभागिता order_by()

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

उदाहरण के अनुसार, मान लीजिए कि आपके पास एक मॉडल है:

from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField()

    class Meta:
        ordering = ["name"]

यहाँ महत्वपूर्ण हिस्सा name क्षेत्र पर डिफ़ॉल्ट आदेश है। यदि आप गिनना चाहते हैं कि प्रत्येक भिन्न data मान कितनी बार आता है, तो आप यह कोशिश कर सकते हैं:

# Warning: not quite correct!
Item.objects.values("data").annotate(Count("id"))

… जो Item वस्तुओं को उनके सामान्य data मूल्यों द्वारा समूहित करेगा और फिर प्रत्येक समूह में id मानों की संख्या की गणना करेगा। सिवाय इसके कि यह काफी काम नहीं करेगा। name से डिफॉल्ट ऑर्डर करने वाले भी ग्रुपिंग में एक भूमिका निभाएंगे, इसलिए यह क्वेरी अलग (data, name) जोड़े द्वारा ग्रुप करेगा, जो कि आप नहीं चाहते हैं। इसके बजाय, आपको इस क्वेरी का निर्माण करना चाहिए:

Item.objects.values("data").annotate(Count("id")).order_by()

... क्वेरी में किसी भी आदेश को समाशोधन। आप बिना किसी हानिकारक प्रभाव के भी data कह सकते हैं, क्योंकि यह पहले से ही क्वेरी में भूमिका निभा रहा है।

यह व्यवहार वैसा ही है जैसा कि distinct() लिए क्वेरी दस्तावेज़ में नोट किया गया है और सामान्य नियम समान है: आम तौर पर आप परिणाम में एक अतिरिक्त भूमिका निभाने वाले अतिरिक्त कॉलम नहीं चाहेंगे, इसलिए आदेश को साफ़ करें, या कम से कम सुनिश्चित करें यह केवल उन क्षेत्रों तक ही सीमित है जिन्हें आप एक values() कॉल में भी चुनते हैं।

ध्यान दें

आप यथोचित पूछ सकते हैं कि Django आपके लिए बाहरी कॉलम क्यों नहीं हटाता है। मुख्य कारण distinct() और अन्य स्थानों के साथ संगति है: Django कभी भी आपके द्वारा निर्दिष्ट किए गए आदेशों की कमी को दूर नहीं करता है (और हम उन अन्य तरीकों के व्यवहार को नहीं बदल सकते हैं, क्योंकि यह हमारी एपीआई स्थिरता नीति का उल्लंघन करेगा)।

एकत्रीकरण एनोटेशन

आप एक एनोटेशन के परिणाम पर एक कुल भी उत्पन्न कर सकते हैं। जब आप एक aggregate() खंड को परिभाषित करते हैं, तो आप जो समुच्चय प्रदान करते हैं, वह क्वेरी में annotate() खंड के भाग के रूप में परिभाषित किसी अन्य उपनाम को संदर्भित कर सकता है।

उदाहरण के लिए, यदि आप प्रति पुस्तक लेखकों की औसत संख्या की गणना करना चाहते हैं, तो आप पहले लेखक की गिनती के साथ पुस्तकों के सेट को एनोटेट करते हैं, फिर उस लेखक की गणना को एनोटेशन क्षेत्र को संदर्भित करते हुए एकत्रित करते हैं:

>>> from django.db.models import Avg, Count
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}