Django: समूह द्वारा दिनांक (दिन, माह, वर्ष)


89

मुझे इस तरह एक साधारण मॉडल मिला है:

class Order(models.Model):
    created = model.DateTimeField(auto_now_add=True)
    total = models.IntegerField() # monetary value

और मैं महीने-दर-महीने के ब्रेकडाउन का उत्पादन करना चाहता हूं:

  • एक महीने में कितनी बिक्री हुई ( COUNT)
  • संयुक्त मूल्य ( SUM)

मुझे यकीन नहीं है कि यह हमला करने का सबसे अच्छा तरीका क्या है। मैंने कुछ डरावने दिखने वाले एक्स्ट्रा-सिलेक्टेड क्वेश्चन देखे हैं, लेकिन मेरा सिंपल माइंड मुझे बता रहा है कि मैं सिर्फ इटरेटिंग नंबर्स से बेहतर हो सकता हूं, एक अनियंत्रित शुरुआत से शुरू होने वाला साल / महीना और जब तक मैं मौजूदा महीने तक पहुंचता हूं, सिंपल फेंकना शुरू कर दूंगा उस महीने के लिए फ़िल्टरिंग प्रश्न। अधिक डेटाबेस काम - कम डेवलपर तनाव!

आपके लिए सबसे ज्यादा क्या मायने रखता है? वहाँ एक अच्छा तरीका है मैं डेटा की एक त्वरित तालिका वापस खींच सकता है? या मेरी गंदी विधि शायद सबसे अच्छा विचार है?

मैं Django 1.3 का उपयोग कर रहा हूं। यकीन नहीं होता कि उन्होंने GROUP_BYहाल ही में एक अच्छा तरीका जोड़ा है ।


जवाबों:


219

Django 1.10 और ऊपर

Django प्रलेखन सूचियों extraके रूप में जल्दी ही बहिष्कृत कर । (यह इंगित करने के लिए धन्यवाद कि @ आउटडैम, @ लुकास 03)। मैंने एक टिकट खोला और यह वह समाधान है जो जर्षा ने प्रदान किया।

from django.db.models.functions import TruncMonth
from django.db.models import Count

Sales.objects
    .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
    .values('month')                          # Group By month
    .annotate(c=Count('id'))                  # Select the count of the grouping
    .values('month', 'c')                     # (might be redundant, haven't tested) select month and count 

पुराने संस्करण

from django.db import connection
from django.db.models import Sum, Count

truncate_date = connection.ops.date_trunc_sql('month', 'created')
qs = Order.objects.extra({'month':truncate_date})
report = qs.values('month').annotate(Sum('total'), Count('pk')).order_by('month')

संपादित करता

  • जोड़ा गया गिनती
  • Django> = 1.10 के लिए जानकारी जोड़ी गई

1
क्या डेटाबेस बैकएंड प्रयोग कर रहे हैं - यह postgres में ठीक काम करता है>>> qs.extra({'month':td}).values('month').annotate(Sum('total')) [{'total__sum': Decimal('1234.56'), 'month': datetime.datetime(2011, 12, 1, 0, 0)}]
tback

1
@seddonym फिक्स्ड (करने के लिए jarshwah धन्यवाद)
tback

1
ट्रुंगकॉम्पटन Django 1.8 में उपलब्ध नहीं है
सुधाकरन पैकियाथन

2
धन्यवाद, महान काम करता है। पूर्व -10 संस्करण के लिए कोने का मामला: यदि कोई अन्य मॉडल पर जुड़ता है / फ़िल्टर होता है, जिसमें समान फ़ील्ड हो सकता है (जैसे: टाइमस्टैम्प), तो किसी को फ़ील्ड को पूरी तरह से योग्य बनाना होगा -'{}.timestamp'.format(model._meta.db_table)
zsepi

1
बस एक त्वरित ध्यान दें कि यदि Django USE_TZसेटिंग है True, तो दो संस्करण बिल्कुल बराबर नहीं हैं। उपयोग करने वाला संस्करण TruncMonthटाइमस्टैम्प को ट्रंकटिंग से TIME_ZONEपहले सेटिंग द्वारा निर्दिष्ट समय क्षेत्र में बदल देगा , जबकि उपयोग करने वाला संस्करण date_trunc_sqlडेटाबेस में कच्चे यूटीसी टाइमस्टैम्प को छोटा कर देगा।
डैनियल हार्डिंग

32

@Tback उत्तर के लिए बस एक छोटा सा जोड़: यह मेरे लिए Django 1.10.6 के साथ काम नहीं करता और पोस्टग्रेज करता है। मैंने इसे ठीक करने के लिए आदेश_बी () जोड़ा।

from django.db.models.functions import TruncMonth
Sales.objects
    .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
    .values('month')                          # Group By month
    .annotate(c=Count('id'))                  # Select the count of the grouping
    .order_by()

1
yup: docs.djangoproject.com/en/1.11/topics/db/aggregation/… ... अच्छे डिजाइन की तरह नहीं लगता है, लेकिन वे बहुत चतुर हैं उन django लोग, तो यह वास्तव में है।
विलियम्स

TruncDateआपको दिनांक (महीने का दिन) के लिए समूह बनाने की अनुमति देता है
नील

10

एक और दृष्टिकोण का उपयोग करना है ExtractMonth। मैं केवल एक डेटाइम वर्ष मान लौटाए जाने के कारण TruncMonth का उपयोग करके समस्या में चला गया। उदाहरण के लिए, 2009 में केवल महीने लौटाए जा रहे थे। ExtractMonth ने इस समस्या को पूरी तरह से ठीक किया और इसे नीचे की तरह उपयोग किया जा सकता है:

from django.db.models.functions import ExtractMonth
Sales.objects
    .annotate(month=ExtractMonth('timestamp')) 
    .values('month')                          
    .annotate(count=Count('id'))                  
    .values('month', 'count')  

2
    metrics = {
        'sales_sum': Sum('total'),
    }
    queryset = Order.objects.values('created__month')
                               .annotate(**metrics)
                               .order_by('created__month')

querysetआदेश की एक सूची, प्रति माह एक लाइन, बिक्री की राशि के संयोजन है:sales_sum

@ डेंगू 2.1.7


1

यहाँ मेरा गंदा तरीका है। यह गंदा है।

import datetime, decimal
from django.db.models import Count, Sum
from account.models import Order
d = []

# arbitrary starting dates
year = 2011
month = 12

cyear = datetime.date.today().year
cmonth = datetime.date.today().month

while year <= cyear:
    while (year < cyear and month <= 12) or (year == cyear and month <= cmonth):
        sales = Order.objects.filter(created__year=year, created__month=month).aggregate(Count('total'), Sum('total'))
        d.append({
            'year': year,
            'month': month,
            'sales': sales['total__count'] or 0,
            'value': decimal.Decimal(sales['total__sum'] or 0),
        })
        month += 1
    month = 1
    year += 1

वहाँ अच्छी तरह से साल / महीने पाशन का एक बेहतर तरीका हो सकता है, लेकिन यह वास्तव में मुझे परवाह नहीं है :)


BTW यह ठीक काम करेगा, लेकिन आप जानते हैं कि महीनों में लूप भी एक महान विचार नहीं है। क्या होगा अगर कोई इसे एक महीने के दिन में बनाना चाहता है, तो यह लूप 30-31 दिनों तक फैल जाएगा। अन्यथा इसका काम ठीक है
मयंक प्रताप सिंह

यदि आपके पास लाखों रिकॉर्ड हैं तो यह बहुत धीमा है
jifferent

@ जिफ़र बिल्कुल! मैंने इसे यह दिखाने के लिए जोड़ा कि प्रश्न पोस्ट करने के समय मेरा समाधान क्या था। अन्य उत्तर बहुत बेहतर हैं।
ओली

0

यहां बताया गया है कि आप समय की मनमानी करके डेटा को कैसे समूहीकृत कर सकते हैं:

from django.db.models import F, Sum
from django.db.models.functions import Extract, Cast
period_length = 60*15 # 15 minutes

# Annotate each order with a "period"
qs = Order.objects.annotate(
    timestamp=Cast(Extract('date', 'epoch'), models.IntegerField()),
    period=(F('timestamp') / period_length) * period_length,
)

# Group orders by period & calculate sum of totals for each period
qs.values('period').annotate(total=Sum(field))

-1

महीने से:

 Order.objects.filter().extra({'month':"Extract(month from created)"}).values_list('month').annotate(Count('id'))

वर्ष तक:

 Order.objects.filter().extra({'year':"Extract(year from created)"}).values_list('year').annotate(Count('id'))

दिवस पश्चात:

 Order.objects.filter().extra({'day':"Extract(day from created)"}).values_list('day').annotate(Count('id'))

काउंट आयात करना न भूलें

from django.db.models import Count

Django के लिए <1.10


3
हाँ, महान अभ्यास, सभी मॉडल से आयात
जेसी रोमाँडे

मैं स्पष्ट रूप से विडंबनापूर्ण था। ऐसा करना एक भयानक अभ्यास है। आपको ऐसा नहीं करना चाहिए और मैंने बस उसी के लिए (जो मैंने नहीं किया)
जेसी रोकोमांटे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.