Django में क्वेरीसेट से पहली वस्तु प्राप्त करने का सबसे तेज़ तरीका?


193

अक्सर मैं खुद को Django में एक क्वेरीसेट से पहली वस्तु प्राप्त करना चाहता हूं, या Noneअगर कोई नहीं है तो वापस लौटना चाहता हूं । ऐसा करने के बहुत सारे तरीके हैं जो सभी काम करते हैं। लेकिन मैं सोच रहा हूं कि कौन सा कलाकार सबसे अच्छा है।

qs = MyModel.objects.filter(blah = blah)
if qs.count() > 0:
    return qs[0]
else:
    return None

क्या इसका परिणाम दो डेटाबेस कॉल में होता है? जो बेकार लगता है। क्या यह कोई तेज है?

qs = MyModel.objects.filter(blah = blah)
if len(qs) > 0:
    return qs[0]
else:
    return None

एक और विकल्प होगा:

qs = MyModel.objects.filter(blah = blah)
try:
    return qs[0]
except IndexError:
    return None

यह एक एकल डेटाबेस कॉल उत्पन्न करता है, जो अच्छा है। लेकिन बहुत समय के लिए एक अपवाद ऑब्जेक्ट बनाने की आवश्यकता होती है, जो कि एक बहुत ही स्मृति-गहन चीज है जब आपको वास्तव में आवश्यकता होती है एक तुच्छ इफ-टेस्ट।

मैं केवल एकल डेटाबेस कॉल के साथ और अपवाद वस्तुओं के साथ स्मृति को मंथन किए बिना कैसे कर सकता हूं?


21
अंगूठे का नियम: यदि आप DB राउंड-ट्रिप को कम करने के बारे में चिंतित हैं, तो len()क्वेरीसेट पर उपयोग न करें , हमेशा उपयोग करें .count()
डैनियल डिपोलो

7
"एक अपवाद वस्तु को बनाने में बहुत समय लगता है, जो एक बहुत ही स्मृति-गहन चीज है" - यदि आप एक अतिरिक्त अपवाद बनाने के बारे में चिंतित हैं, तो आप इसे गलत कर रहे हैं क्योंकि पायथन अपवादों का उपयोग सभी जगह करता है। क्या आपने वास्तव में यह मान लिया है कि यह आपके मामले में स्मृति-गहन है?
5

1
@ Leopd और यदि आप वास्तव में किसी भी तरह से (या कम से कम टिप्पणी) awser को बेंचमार्क कर रहे हैं, तो आपको पता होगा कि यह किसी भी तेजी से नहीं है। यह वास्तव में धीमा हो सकता है, 'अपने अतिरिक्त सूची बनाने का कारण सिर्फ इसे बाहर फेंकना है। और वह सब सिर्फ एक अजगर समारोह को कॉल करने या पहली जगह में Django के ORM का उपयोग करने की लागत की तुलना में मूंगफली है! फ़िल्टर करने के लिए एक एकल कॉल () कई है, कई, कई बार धीमी गति से फिर एक अपवाद को बढ़ाता है (जो अभी भी उठाया जा रहा है, 'कारण है कि कैसे इटेरेटर प्रोटोकॉल काम करता है!)।
22

1
आपका अंतर्ज्ञान सही है कि प्रदर्शन अंतर छोटा है, लेकिन आपका निष्कर्ष गलत है। मैंने एक मानदंड चलाया और स्वीकृत उत्तर वास्तव में एक वास्तविक मार्जिन से तेज है। जाओ पता लगाओ।
तेंदुआ

11
Django 1.6 का उपयोग करने वाले लोगों के लिए, उन्होंने अंततः first()और last()सुविधा विधियों को जोड़ा है : docs.djangoproject.com/en/dev/ref/models/querysets/#first
वी येन

जवाबों:


328

Django 1.6 (जारी 2013 नवंबर) ने सुविधा के तरीकों को पेश किया first()और last()जो परिणामस्वरूप अपवाद Noneको निगलता है और यदि क्वेरी कोई ऑब्जेक्ट नहीं देता है तो वापस लौटें ।


1
यह [: 1] नहीं करता है, इसलिए यह उतना तेज़ नहीं है (जब तक आपको पूरे क्वेरीसेट का मूल्यांकन करने की आवश्यकता न हो)।
जनेक ३:

13
यह भी, first()और एक क्वेरी पर एक खंड last()लागू करता है ORDER BY। यह परिणामों को निर्धारक बना देगा लेकिन संभवतः क्वेरी को धीमा कर देगा।
फिल क्रिलोव

@ Janek37 प्रदर्शन में कोई अंतर नहीं हैं। जैसा कि cod3monk3y द्वारा इंगित किया गया है, यह एक सुविधाजनक तरीका है और यह पूरी क्वेरी को नहीं पढ़ता है।
ज़ोमपा

142

सही जवाब है

Entry.objects.all()[:1].get()

जो में इस्तेमाल किया जा सकता है:

Entry.objects.filter()[:1].get()

आप इसे पहले एक सूची में बदलना नहीं चाहेंगे, क्योंकि यह सभी रिकॉर्ड्स के पूर्ण डेटाबेस कॉल को बाध्य करेगा। बस ऊपर करो और यह केवल पहले खींचेगा। आप .order_byयह सुनिश्चित करने के लिए भी उपयोग कर सकते हैं कि आप जो चाहते हैं वह आपको पहले मिल जाए।

जोड़ना सुनिश्चित करें .get()अन्यथा आपको एक क्वेरीसैट वापस मिलेगा और एक वस्तु नहीं।


9
आपको अभी भी इसे एक कोशिश में लपेटने की आवश्यकता होगी ... ObjectDoesNotExist को छोड़कर, जो मूल तीसरे विकल्प की तरह है लेकिन स्लाइसिंग के साथ।
डैनी डब्ल्यू। अडैर

1
यदि आप कॉल करने वाले हैं तो एक लिमिट सेट करने का क्या मतलब है? ORM और SQL कंपाइलर को यह तय करने दें कि यह बैकेंड के लिए सबसे अच्छा क्या है (उदाहरण के लिए, Oracle Django ने LIMIT का अनुकरण किया है, इसलिए यह मदद करने के बजाय चोट पहुंचाएगा)।
lqc

मैंने इस उत्तर का उपयोग ट्रेलिंग .get () के बिना किया। यदि कोई सूची वापस की जाती है तो मैं सूची का पहला तत्व वापस करता हूं।
कीथ जॉन हचिसन

क्या अलग है होने का Entry.objects.all()[0]??
जेम्स लिन

15
@JamesLin अंतर यह है कि [: 1] .get () DoNotExist उठाता है, जबकि [0] IndexError उठाता है।
रोपेज़

49
r = list(qs[:1])
if r:
  return r[0]
return None

1
यदि आप ट्रेसिंग चालू करते हैं तो मुझे पूरा यकीन है कि आप इस LIMIT 1प्रश्न को जोड़ भी पाएंगे , और मुझे नहीं पता कि आप इससे बेहतर कोई और कर सकते हैं। हालांकि, आंतरिक रूप __nonzero__में QuerySetके रूप में कार्यान्वित किया जाता है try: iter(self).next() except StopIteration: return false...तो यह अपवाद बच नहीं करता है।
बेन जैक्सन

@Ben: QuerySet.__nonzero__()कभी नहीं कहा जाता है क्योंकि ट्रूनेस के लिए जाँच से पहले QuerySetपरिवर्तित किया जाता है list। अन्य अपवाद अभी भी हो सकते हैं।
इग्नासियो वाज़क्वेज़-अब्राम्स

@ एरन: जो एक StopIterationअपवाद उत्पन्न कर सकता है।
इग्नासियो वाज़क्वेज़-अब्राम्स

__iter__एक नया पुनरावृत्ति वस्तु प्राप्त करने के लिए सूची === कॉल करने के लिए परिवर्तित और फेंक दिया जाता है nextजब तक यह विधि StopIterationहै। तो निश्चित रूप से कहीं अपवाद होने वाला है;)
lqc

14
यह उत्तर अब पुराना हो गया है, Django 1.6+ के लिए @ Cod3monk3y उत्तर पर एक नज़र डालें
ValAyal

37

अब, Django 1.9 में आपके पास first() क्वेरीसेट के लिए विधि है।

YourModel.objects.all().first()

यह तुलना में बेहतर तरीका है .get()या [0]क्योंकि यह अपवाद नहीं छोड़ता है यदि क्वेरी खाली है, तोराहोफ़, आपको उपयोग करने की आवश्यकता नहीं हैexists()


1
यह SQL में एक LIMIT 1 का कारण बनता है और मैंने दावा किया है कि यह क्वेरी को धीमा कर सकता है - हालांकि मैं यह देखना चाहता हूं कि इस बात की पुष्टि की जाए: यदि क्वेरी केवल एक आइटम लौटाती है, तो LIMIT 1 वास्तव में प्रदर्शन को प्रभावित क्यों करेगा? इसलिए मुझे लगता है कि उपर्युक्त उत्तर ठीक है, लेकिन साक्ष्यों को देखना अच्छा लगेगा।
रौज़ेना

मैं "बेहतर" नहीं कहूंगा। यह वास्तव में आपकी उम्मीदों पर निर्भर करता है।
ट्रिगर

7

यदि आप अक्सर पहला तत्व प्राप्त करने की योजना बनाते हैं - तो आप इस दिशा में क्वेरीसेट का विस्तार कर सकते हैं:

class FirstQuerySet(models.query.QuerySet):
    def first(self):
        return self[0]


class ManagerWithFirstQuery(models.Manager):
    def get_query_set(self):
        return FirstQuerySet(self.model)

मॉडल को इस तरह परिभाषित करें:

class MyModel(models.Model):
    objects = ManagerWithFirstQuery()

और इसे इस तरह से उपयोग करें:

 first_object = MyModel.objects.filter(x=100).first()

ऑब्जेक्ट्स को कॉल करें = ManagerWithFirstQuery को ऑब्जेक्ट्स के रूप में = ManagerWithFirstQuery () - नॉट फॉरगेट PARESHESES - वैसे भी, आपने मेरी इतनी +1 मदद की
Kamil

7

यह भी काम कर सकता है:

def get_first_element(MyModel):
    my_query = MyModel.objects.all()
    return my_query[:1]

यदि यह खाली है, तो एक खाली सूची लौटाता है, अन्यथा यह सूची के अंदर पहला तत्व देता है।


1
यह अब तक का सबसे अच्छा समाधान है ... डेटाबेस में केवल एक कॉल का परिणाम है
Shh


3

आपको django विधियों का उपयोग करना चाहिए, जैसे मौजूद है। वहाँ इसका उपयोग करने के लिए आप के लिए।

if qs.exists():
    return qs[0]
return None

1
सिवाय, अगर मैं इसे सही ढंग से समझता हूं, तो मुहावरेदार पायथन आमतौर पर लुक बिफोर यू लीप दृष्टिकोण के बजाय अनुमति ( ईएएफपी ) दृष्टिकोण के लिए माफ़ी मांगने के लिए एक आसान का उपयोग करता है ।
बिगस्मोक

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

2

Django 1.6 के बाद से आप पहले () विधि के साथ फिल्टर () का उपयोग कर सकते हैं:

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