2013-03-01 11 views
7

मान लें कि मैं अपने नवीनतम स्प्रिंट समय द्वारा आदेशित धावकों की एक सूची दिखाना चाहता हूं।Django: नवीनतम बाल मॉडल फ़ील्ड के आधार पर एक क्वेरीसेट का ऑर्डर करना

class Runner(models.Model): 
    name = models.CharField(max_length=255) 

class Sprint(models.Model): 
    runner = models.ForeignKey(Runner) 
    time = models.PositiveIntegerField() 
    created = models.DateTimeField(auto_now_add=True) 

यह मैं क्या एसक्यूएल में करना होगा की एक त्वरित स्केच है:

SELECT runner.id, runner.name, sprint.time 
FROM runner 
LEFT JOIN sprint ON (sprint.runner_id = runner.id) 
WHERE 
    sprint.id = (
    SELECT sprint_inner.id 
    FROM sprint as sprint_inner 
    WHERE sprint_inner.runner_id = runner.id 
    ORDER BY sprint_inner.created DESC 
    LIMIT 1 
) 
    OR sprint.id = NULL 
ORDER BY sprint.time ASC 

Django QuerySet documentation कहता है:

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

मुझे लगता है मैं यहाँ कुछ फिल्टर लागू करने की आवश्यकता है, लेकिन मुझे यकीन है कि वास्तव में क्या Django उम्मीद नहीं कर रहा हूँ ...

एक टिप्पणी है, क्योंकि यह इस उदाहरण में स्पष्ट नहीं है: धावक तालिका कई होगा सौ प्रविष्टियां, स्पिंट्स में कई सैकड़ों होंगे और कुछ दिनों में शायद कई हजार प्रविष्टियां होंगी। डेटा एक पृष्ठांकित दृश्य में प्रदर्शित किया जाएगा, इसलिए पायथन में सॉर्टिंग एक विकल्प नहीं है।

एकमात्र अन्य संभावना जो मैं देखता हूं वह स्वयं एसक्यूएल लिख रहा है, लेकिन मैं इसे हर कीमत से बचना चाहता हूं।

उत्तर

2

मैं वहाँ केवल एक क्वेरी के साथ ORM के माध्यम से ऐसा करने का तरीका है नहीं लगता है, तो आप दूसरे स्थान की एक सूची हड़पने और उनके नवीनतम स्प्रिंट आईडी के जोड़ने के लिए annotate इस्तेमाल कर सकते हैं - तो फिल्टर करने और उन स्प्रिंट आदेश ।

>>> from django.db.models import Max 

# all runners now have a `last_race` attribute, 
# which is the `id` of the last sprint they ran 
>>> runners = Runner.objects.annotate(last_race=Max("sprint__id")) 

# a list of each runner's last sprint ordered by the the sprint's time, 
# we use `select_related` to limit lookup queries later on 
>>> results = Sprint.objects.filter(id__in=[runner.last_race for runner in runners]) 
...       .order_by("time") 
...       .select_related("runner") 

# grab the first result 
>>> first_result = results[0] 

# you can access the runner's details via `.runner`, e.g. `first_result.runner.name` 
>>> isinstance(first_result.runner, Runner) 
True 

# this should only ever execute 2 queries, no matter what you do with the results 
>>> from django.db import connection 
>>> len(connection.queries) 
2 

यह बहुत तेज है और अभी भी डेटाबेस के सूचकांक और कैशिंग का उपयोग करेगा।

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

+0

क्या यह अपेक्षाकृत उच्च स्मृति उपयोग नहीं करता है? जहां तक ​​मैं देख सकता हूं कि यह कम से कम प्रत्येक धावक को स्मृति में खींचता है और अपनी स्प्रिंट आईडी की एक बड़ी सूची बनाता है। डीबी में कई सौ धावकों के साथ हर पृष्ठ दृश्य पर ऐसा करने से मुझे थोड़ा * असहज महसूस होता है। यह वह जगह है जहां कैशिंग काटता है, मुझे लगता है। – Strayer

+1

10,000 धावकों के साथ इसका परीक्षण करने के बाद यह रैम के 10 एमबी (3 एमबी वास्तव में ...) से कम इस्तेमाल किया। यदि आपको लगता है कि आपको इससे अधिक की आवश्यकता होगी, तो आपको वास्तव में कच्चे एसक्यूएल का उपयोग करना चाहिए। हमेशा की तरह, इसका सबसे अच्छा तरीका पहले प्रोफाइल करना है - अटकलें नहीं। समयपूर्व अनुकूलन और यह सब ... – Matt

+0

और, कुछ सौ रिकॉर्ड वास्तव में बहुत कुछ नहीं है ... निश्चित रूप से प्रदर्शन अनुकूलन के बारे में चिंता करने के लिए पर्याप्त नहीं है। कुछ सौ हजार रिकॉर्ड आमतौर पर होते हैं जहां आप इसके बारे में सोचना शुरू कर देंगे, और फिर भी आमतौर पर यह कोई मुद्दा नहीं है (इंडेक्स या दो में टॉस करें और यह हल हो गया है)। – Matt

0
def view_name(request): 
    spr = Sprint.objects.values('runner', flat=True).order_by(-created).distinct() 
    runners = [] 
    for s in spr: 
     latest_sprint = Sprint.objects.filter(runner=s.runner).order_by(-created)[:1] 
     for latest in latest_sprint: 
      runners.append({'runner': s.runner, 'time': latest.time}) 

    return render(request, 'page.html', { 
      'runners': runners, 
    }) 


{% for runner in runners %} 
    {{runner.runner}} - {{runner.time}} 
{% endfor %} 
+0

समस्या को नवीनतम स्प्रिंट नहीं मिल रहा है लेकिन रनर क्वेरीसेट को अपने नवीनतम स्प्रिंट 'टाइम' फ़ील्ड द्वारा ऑर्डर करना है। – Strayer

+0

यह काम करता है, हाँ। समस्या यह है कि यह धावकों को एप्लिकेशन में ऑर्डर करने में मदद करता है, जो कम से कम एक बड़ी मेमोरी उपयोग और अपेक्षाकृत उच्च CPU उपयोग का कारण बनता है। तालिका आकार के बारे में अद्यतन प्रश्न देखें। इस दृष्टिकोण के साथ एक और समस्या यह है कि यह किसी भी धावक को नहीं दिखाएगा जिसमें कोई स्प्रिंट नहीं है। हालांकि इसे पायथन कोड के भीतर भी हल किया जा सकता है, यह डेटाबेस के लिए एक आदर्श काम है क्योंकि यह इसके सूचकांक और कैश का उपयोग कर सकता है। यह छोटे डेटाबेस के लिए काम करता है, लेकिन अगर मैं इसे इस तरह से करता हूं तो हमारा SysAdmin मुझे मार देगा;) – Strayer

+0

हमम ... यह मुश्किल है। और हम वही हैं, मैं अपने नियोक्ता की अपेक्षा के कारण मेरे काम के बारे में कोडिंग में सावधान हूं। :) – catherine

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