2015-09-02 7 views
6

यहाँ के भीतर अधिकतम मूल्य के साथ रिकॉर्ड का चयन करता है मेरी Django वर्ग है बनाने के लिए:कैसे Django क्वेरीसमूह है कि एक समूह

class MyClass(models.Model): 
    my_integer = models.IntegerField() 
    created_ts = models.DateTimeField(default=datetime.utcnow, editable=False) 

मुझे लगता है कि my_integer के प्रत्येक अद्वितीय मान के नवीनतम created_ts है MyClass के उदाहरण पुनः प्राप्त करना चाहते । मैं यह नहीं समझ सकता कि इसे कैसे किया जाए।

कोई मुझे दिखा सकता है कि यह कैसे करें?

+0

'नवीनतम' का मतलब केवल एक है। – Gocht

+1

"my_integer के प्रत्येक अद्वितीय मान के लिए नवीनतम बनाए गए_ट्स"। इसका मतलब है कि एक से अधिक। –

+1

@ सक्बिबाली आपका प्रश्न बताता है "मैं 'माइक्लास' [...]" के उदाहरणों को पुनर्प्राप्त करना चाहता हूं, लेकिन आपने एक ऐसा उत्तर स्वीकार कर लिया है जो 'MyClass' के उदाहरणों को पुनर्प्राप्त नहीं करता है। यह शब्दकोश देता है। आप भी [टिप्पणी] (http://stackoverflow.com/questions/32359954/how-to-make-django-queryset-that-selects-records-with-max-value-within-a-group#comment52598332_32361355) दूसरे पर जवाब दें कि आप कक्षा के उदाहरण चाहते हैं। – Louis

उत्तर

0

अपरीक्षित

results = MyClass.objects.all().distinct('my_integer').order_by('created_ts') 
+0

इसने मुझे अपवाद दिया: 'NotImplementedError: फ़ील्ड पर DISTINCT इस डेटाबेस बैकएंड द्वारा समर्थित नहीं है। एफवाईआई, मैं mysql का उपयोग कर रहा हूँ। –

0
MyClass.objects.order_by('my_integer', '-created_ts').distinct('my_integer') 

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

+0

इसने मुझे अपवाद दिया: 'NotImplementedError: फ़ील्ड पर DISTINCT इस डेटाबेस बैकएंड द्वारा समर्थित नहीं है। एफवाईआई, मैं mysql का उपयोग कर रहा हूँ। –

+0

इसका संदर्भ लें, http://stackoverflow.com/questions/12402923/django-mysql-distinct-query-for-getting- कई अलग-अलग माइस्क्ल बैकएंड 'विशिष्ट()' ऑपरेशन का समर्थन नहीं करता है। इसके बराबर (जहां तक ​​मुझे पता है) 'MyClass.objects.values ​​(' my_integer ',' create_ts ')। विशिष्ट()। Order_by (' - created_ts ') '। इसका परीक्षण किया और यह उम्मीद के अनुसार काम करता है। – ianveshi

+0

धन्यवाद ianveshi। यह मुझे करीब ले जाता है। लेकिन अभी भी सही नहीं है। मुझे MyClass के वास्तविक उदाहरण प्राप्त करने की आवश्यकता है जो इस मानदंड को पूरा करते हैं। आपका समाधान केवल 'create_ts' के मान' my_integer' के मान देता है जिसके लिए यह मान्य है। आपको यह मानना ​​है कि वास्तविकता में MyClass केवल 'my_integer' और' create_ts' की तुलना में एक बहुत ही जटिल वर्ग है। मुझे पूरी चीज चाहिए। कैसे? –

0

इसे आजमाएं;

from django.db.models import Max 

MyClass.objects.values('my_integer').annotate(Max('created_ts')) 
+0

यह हमें केवल 'my_integer'' और 'बनाई गई_ts__max'' वस्तुओं के मान देता है और वास्तविक उदाहरण नहीं देता है। – maxsocl

0

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

values = MyClass.objects.order_by('-created_ts').all() 

filtered = [] 
existing = [] 
for value in values: 
    if value.my_integer not in existing: 
     existing.append(value.my_integer) 
     filtered.append(value) 

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

संपादित

यहाँ एक बहुत तेजी से संस्करण है।

def iter_tools(): 
    import itertools 
    qs = MyClass.objects.all() 
    filtered = [] 
    group_by = itertools.groupby(qs, lambda x: x.my_integer) 
    for x in group_by: 
     filtered.append(sorted(x[1], key=lambda x: x.created_ts, reverse=True)[0]) 
    return filtered 

अनिवार्य रूप से जिस तरह से यह काम कर रहा है अपने डाटाबेस से अपने वस्तुओं के सभी हो रही है, पूर्णांक द्वारा उन्हें समूहीकरण, तो टाइमस्टैम्प के आधार पर प्रत्येक समूह छंटाई और बस प्रत्येक समूह से पहले एक हो रही। इसे और भी तेज़ करना मेरे कौशल से परे है लेकिन मुझे यकीन है कि कुछ तरीके हैं।

In[]: timeit.timeit(manual, number=1500) 
Out[]: 0.5577559471130371 
In[]: timeit.timeit(iter_tools, number=1500) 
Out[]: 0.39012885093688965 
----------------------------------------------- 
In[]: timeit.timeit(manual, number=5000) 
Out[]: 1.770777940750122 
In[]: timeit.timeit(iter_tools, number=5000) 
Out[]: 1.2411231994628906 

संपादित करें 2: मैं डेटाबेस के लिए 60000 ऑब्जेक्ट का निर्माण कुछ के साथ इसे आज़माने के लिए

यहाँ बनाम DB में 6 केवल तरह प्रविष्टियों के साथ पहले भी इस एक के timeit है डेटा। मैंने django-fixtureless के साथ डेटा जेनरेट किया है, इसलिए पूर्णांक पूरी तरह यादृच्छिक हैं और उनमें से सभी पर टाइमस्टैम्प प्रत्येक ऑब्जेक्ट के लिए एक नया datetime.now() है।

In[]: timeit.timeit(manual, number=1) 
Out[]: 11.946185827255249 
In[]: timeit.timeit(iter_tools, number=1) 
Out[]: 0.7811920642852783 
In[]: timeit.timeit(iter_tools, number=100) 
Out[]: 77.93837308883667 
In[]: MyClass.objects.all().count() 
Out[]: 60000 

डीबी के बारे में एक नोट: उपरोक्त उदाहरण में मैं सिर्फ अपनी स्थानीय मशीन पर sqlite3 का उपयोग कर रहा था। मैं अभी एक वीएम के रूप में एक त्वरित छोटा mysql सर्वर सेटअप और एक बेहतर गति परिणाम प्राप्त किया।

In[16]: MyClass.objects.all().count() 
Out[16]: 60000 
In[17]: timeit.timeit(iter_tools, number=100) 
Out[17]: 49.636733055114746 
In[18]: timeit.timeit(iter_tools, number=1) 
Out[18]: 0.4923059940338135 

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

+0

तो Django क्वेरीसेट का उपयोग कर डेटाबेस द्वारा सॉर्टिंग/ग्रुपिंग को मूल रूप से नहीं किया जा सकता है? मैं ऐसा करने के लिए कोड लिखने से बचने की उम्मीद कर रहा था। मुझे लगता है कि डीबी होने से यह अधिक कुशल होगा। –

+0

मेरा नवीनतम संपादन देखें, मैंने कुछ बार एक वास्तविक mysql डेटाबेस के साथ पोस्ट किया है। लेकिन आपको जवाब देने के लिए, मुझे सीधे कस्टम एसक्यूएल के बिना डीबी से ऐसा करने के किसी भी तरीके से अवगत नहीं है। लेकिन इसका मतलब यह नहीं है कि तरीके नहीं हैं। मैं mysql और django के संयोजन के साथ काफी अनुभवहीन हूं इसलिए ऐसा करने के कुछ तरीके हो सकते हैं। हालांकि, इसे सिर्फ कोड में करने के लिए django की कुछ जटिल छिपी हुई विशेषता को खोजने की कोशिश करना आसान हो सकता है। – electrometro

+0

भूलें कि आप इन ऑब्जेक्ट्स को वास्तव में अक्सर बना रहे हैं, तो आप अधिक समय के लिए क्वेरीसेट में परिणामों को फ़िल्टर कर सकते हैं। उदाहरण, यदि आपको पता है कि आपको आवश्यक सभी परिणामों को पिछले 5 मिनट के भीतर कभी-कभी होने जा रहे हैं, तो आप क्यूएस में एक फ़िल्टर जोड़ सकते हैं और इससे फंक्शंस को लूप करने के लिए ऑब्जेक्ट्स की मात्रा को बहुत कम कर दिया जाएगा। सही उत्तर वास्तव में आपके डेटा के आकार के आधार पर अलग-अलग होगा, कितनी बार सभी डेटा अपडेट किया जाता है, कितनी बार परिणामों की गणना की आवश्यकता होती है और इसे कितनी तेज़ी से करने की आवश्यकता होती है। – electrometro

3

यह तुम्हारी मदद करेगा तालिका में

from django.db.models import Count, Max 
MyClass.objects.values('my_integer').annotate(count=Count("my_integer"),latest_date=Max('created_ts')) 

डाटा

my_integer  created_ts 
    -    ----------- 
    1 2015-09-08 20:05:51.144321+00:00 
    1 2015-09-08 20:08:40.687936+00:00 
    3 2015-09-08 20:08:58.472077+00:00 
    2 2015-09-08 20:09:08.493748+00:00 
    2 2015-09-08 20:10:20.906069+00:00 

आउटपुट

[ 
    {'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 40, 687936, tzinfo=<UTC>), 'my_integer': 1}, 
    {'count': 2, 'latest_date': datetime.datetime(2015, 9, 8, 20, 10, 20, 906069, tzinfo=<UTC>), 'my_integer': 2}, 
    {'count': 1, 'latest_date': datetime.datetime(2015, 9, 8, 20, 8, 58, 472077, tzinfo=<UTC>), 'my_integer': 3} 
] 
0

आप एक आर कर सकते हैं या तो aw क्वेरी:

MyClass.objects.raw(""" 
SELECT m1.id, m1.my_integer, m1.created_ts 
FROM app_myclass AS m1, (
    SELECT my_integer, MAX(created_ts) AS max_created_ts 
    FROM app_myclass 
    GROUP BY my_integer 
) AS m2 
WHERE m1.my_integer = m2.my_integer AND m1.created_ts = m2.max_created_ts 
""")) 

या Django ORM का उपयोग करें:

MyClass.objects.filter(
    created_ts__in=MyClass.objects.values(
     "my_integer" 
    ).annotate(
     created_ts=models.Max(
      "created_ts" 
     ) 
    ).values_list("created_ts", flat=True) 
) 

ध्यान दें कि यह केवल एक ही एसक्यूएल अनुरोध की आवश्यकता है, के रूप में आप से पहले और क्वेरी के बाद मुद्रण len(django.db.connection.queries) से देख सकते हैं।

हालांकि, ध्यान दें कि बाद का समाधान केवल तभी काम करता है जब आपकी created_ts विशेषता अद्वितीय होने की गारंटी दी जाती है, जो आपका मामला नहीं हो सकता है।

यदि आप कच्चे प्रश्न या created_ts पर एक इंडेक्स का उपयोग करने के इच्छुक नहीं हैं, तो आपको शायद अन्य उत्तरों द्वारा सुझाए गए अनुसार PostgreSQL और इसकी DISTINCT ON सुविधा का उपयोग शुरू करना चाहिए।

संबंधित मुद्दे