2009-05-12 13 views
14

कहो सेट मैं 2 मॉडल:Django ORM: संबंधित का चयन करना

class Poll(models.Model): 
    category = models.CharField(u"Category", max_length = 64) 
    [...] 

class Choice(models.Model): 
    poll = models.ForeignKey(Poll) 
    [...] 

एक पोल वस्तु देखते हुए, मैं अपने विकल्पों के साथ क्वेरी कर सकते हैं:

poll.choice_set.all() 

लेकिन, क्वेरी करने के लिए एक उपयोगिता समारोह है मतदान के एक सेट से सभी विकल्प?

वास्तव में, मैं निम्नलिखित की तरह कुछ के लिए देख रहा हूँ (जो समर्थित नहीं है, और मैं इसे कैसे हो सकता है की मांग न करें):

polls = Poll.objects.filter(category = 'foo').select_related('choice_set') 
for poll in polls: 
    print poll.choice_set.all() # this shouldn't perform a SQL query at each iteration 

मैं एक (बदसूरत) समारोह मेरी मदद करने के लिए बनाया कि प्राप्त:

def qbind(objects, target_name, model, field_name): 
    objects = list(objects) 
    objects_dict = dict([(object.id, object) for object in objects]) 
    for foreign in model.objects.filter(**{field_name + '__in': objects_dict.keys()}): 
     id = getattr(foreign, field_name + '_id') 
     if id in objects_dict: 
      object = objects_dict[id] 
      if hasattr(object, target_name): 
       getattr(object, target_name).append(foreign) 
      else: 
       setattr(object, target_name, [foreign]) 
    return objects 

जो इस प्रकार प्रयोग किया जाता है:

polls = Poll.objects.filter(category = 'foo') 
polls = qbind(polls, 'choices', Choice, 'poll') 
# Now, each object in polls have a 'choices' member with the list of choices. 
# This was achieved with 2 SQL queries only. 

वहाँ आसान पहले से ही कुछ पी है Django द्वारा rovided? या कम से कम, एक स्निपेट एक ही तरीके से एक बेहतर तरीके से कर रहा है।

आमतौर पर आप इस समस्या को कैसे संभालेंगे?

+0

शायद आपका qbind फ़ंक्शन सबसे अच्छा किया जा सकता है। लेकिन इसे कस्टम प्रबंधक में पैकेज करने का अर्थ हो सकता है - http://docs.djangoproject.com/en/dev/topics/db/managers/#id2 – NathanD

उत्तर

11

अद्यतन: Django 1.4 के बाद से, यह सुविधा इस प्रकार बनाई गई है: prefetch_related देखें।

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

लेकिन शायद आपने यह किया है। तो दूसरा जवाब: qbind() करता है जो आपको करने की आवश्यकता होगी, लेकिन कस्टम क्वेरीरीसेट सबक्लास में पैक किए जाने पर यह अधिक मूर्खतापूर्ण होगा, जिसमें एक साथ प्रबंधक सबक्लास है जो कस्टम क्वेरीसेट के उदाहरण लौटाता है। आदर्श रूप में आप उन्हें किसी भी रिवर्स रिलेशनशिप के लिए सामान्य और पुन: प्रयोज्य बना सकते हैं। तो फिर तुम जैसे कुछ कर सकता है:

Poll.objects.filter(category='foo').fetch_reverse_relations('choices_set') 

प्रबंधक/क्वेरीसमूह तकनीक का एक उदाहरण के लिए, this snippet, जो एक समान समस्या का हल देखते हैं लेकिन जेनेरिक विदेश कुंजी के मामले के लिए, संबंधों रिवर्स नहीं। अपनी समस्या के लिए वास्तव में अच्छा समाधान बनाने के लिए वहां दिखाए गए ढांचे के साथ अपने qbind() फ़ंक्शन के गले को गठबंधन करना बहुत मुश्किल नहीं होगा।

14

मुझे लगता है कि आप क्या कह रहे हैं, "मुझे पोल के एक सेट के लिए सभी विकल्प चाहिए।" यदि हां, तो इस कोशिश:

polls = Poll.objects.filter(category='foo') 
choices = Choice.objects.filter(poll__in=polls) 
+0

+1 मुझे इस सुविधा के बारे में पता नहीं था! कितनी सुरुचिपूर्ण! –

+1

यह मैं 'qbind' फ़ंक्शन की शुरुआत में करता हूं। लेकिन असल में मैं विकल्प * प्रति * चुनावों का सेट चाहता हूं, न कि पसंद का पूरा सेट। उदाहरण के लिए, यदि मैं टेम्पलेट पर चुनावों की सूची प्रदर्शित करना चाहता हूं, तो उनमें से प्रत्येक के लिए विकल्पों के साथ, मैं प्रत्येक मतदान के लिए डेटाबेस हिट नहीं करना चाहता हूं। 'Qbind' फ़ंक्शन का बिंदु यह प्राप्त करने के लिए आपके 'चुनाव' और 'विकल्प' डेटा को एक साथ जोड़ना है। –

1

मुझे लगता है कि तुम क्या करने की कोशिश कर रहे हैं बच्चे डेटा के शब्द "उत्सुक लोड हो रहा है" है - यानी कि प्रत्येक मतदान के लिए बच्चे को सूची (choice_set) लोड कर रहे हैं, लेकिन सभी डीबी को पहली बार पूछताछ करें, ताकि आपको बाद में प्रश्नों का एक गुच्छा बनाना पड़े।

यदि यह सही है, तो क्या आप के लिए है 'select_related' देख रहे हैं - देख https://docs.djangoproject.com/en/dev/ref/models/querysets/#select-related

मैं तुम्हें की कोशिश की 'select_related' लेकिन यह काम नहीं किया देखा। क्या आप 'select_related' और फिर फ़िल्टर करने का प्रयास कर सकते हैं। यह ठीक हो सकता है।


अद्यतन: यह काम नहीं करता है, नीचे टिप्पणियां देखें।

+0

चयन_संबंधित उपयोगी होगा यदि कोई विकल्प के लिए पूछताछ कर रहा था और प्रत्येक संबंधित मतदान को प्रीलोड करना चाहता था। लेकिन यहां, मैं विपरीत चाहता हूं जो select_related द्वारा समर्थित नहीं है (संबंधित SQL क्वेरी के बारे में सोचें, इसे बहुत से डेटा को डुप्लिकेट किए बिना एक क्वेरी में नहीं किया जा सकता है।) यह 2 प्रश्नों के साथ किया जाना चाहिए। –

+0

हाँ, आपका अधिकार। इसे देखने के लिए खेद है। चूंकि क्वेरी का मूल्यांकन होने तक 'choice_set' उपलब्ध नहीं है, इसलिए यह नहीं पहचाना जाता है कि यह भी मौजूद है। फॉलो-अप के लिए – NathanD

16

समय बीत चुका है और यह कार्यक्षमता अब prefetch_related() क्वेरीज़ेट फ़ंक्शन की शुरुआत के साथ Django 1.4 में उपलब्ध है। यह फ़ंक्शन प्रभावी ढंग से सुझाए गए qbind फ़ंक्शन द्वारा किया जाता है। अर्थात। दो प्रश्नों का प्रदर्शन किया जाता है और पाइथन भूमि में शामिल होता है, लेकिन अब यह ओआरएम द्वारा संभाला जाता है।

मूल क्वेरी अनुरोध अब बन जाएगा:

polls = Poll.objects.filter(category = 'foo').prefetch_related('choice_set') 

के रूप में निम्नलिखित कोड नमूना में दिखाया गया है, polls क्वेरीसमूह Poll प्रति सभी Choice वस्तुओं प्राप्त करने के लिए इस्तेमाल किया जा सकता किसी भी आगे डेटाबेस हिट की आवश्यकता के बिना:

for poll in polls: 
    for choice in poll.choice_set: 
     print choice 
+0

+1। Google से इस पृष्ठ पर किसी के लिए ठोकर खाने का सबसे अच्छा समाधान। –

+0

मैं Django 1.6 का उपयोग करता हूं, और पाइथन खोल में उपयोग करते समय टाइप त्रुटि संबंधित प्रबंधक ऑब्जेक्ट को पुन: उपयोग नहीं करता है। मैंने फ्रेडरिक के समान ही काम किया: चॉइस के पास विदेशी कुंजी पोल है, इसलिए 1 से अधिक रिश्ते betw है। मतदान और पसंद – Timo

+0

@ टिमो आपको 'poll.choice_set.all() 'कॉल करना चाहिए। –

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