2011-02-03 14 views
30

मैंने एक मॉडल बनाया है, और मैं इसके लिए डिफ़ॉल्ट/असम्बद्ध मॉडल फॉर्म प्रस्तुत कर रहा हूं। यह अकेले 64 एसक्यूएल प्रश्न उत्पन्न करता है क्योंकि इसमें कुछ विदेशी कुंजी हैं, और बदले में अधिक विदेशी कुंजी हैं।Django: फोर्स से संबंधित चयन करें?

क्या यह को हमेशा (डिफ़ॉल्ट रूप से) करने के लिए मजबूर करना संभव है, इन मॉडलों में से प्रत्येक बार वापस आने पर select_related करें?

उत्तर

41

आप एक कस्टम मैनेजर बना सकते हैं, और इसे हर जगह लागू करने के लिए बस get_queryset ओवरराइड कर सकते हैं। उदाहरण के लिए:

class MyManager(models.Manager): 
    def get_queryset(self): 
     return super(MyManager, self).get_queryset().select_related('foo', 'bar') 

(1,6 Django से पहले, यह get_query_set था)।

class DefaultSelectOrPrefetchManager(models.Manager): 
    def __init__(self, *args, **kwargs): 
     self._select_related = kwargs.pop('select_related', None) 
     self._prefetch_related = kwargs.pop('prefetch_related', None) 

     super(DefaultSelectOrPrefetchManager, self).__init__(*args, **kwargs) 

    def get_queryset(self, *args, **kwargs): 
     qs = super(DefaultSelectOrPrefetchManager, self).get_queryset(*args, **kwargs) 

     if self._select_related: 
      qs = qs.select_related(*self._select_related) 
     if self._prefetch_related: 
      qs = qs.prefetch_related(*self._prefetch_related) 

     return qs 


class Sandwich(models.Model): 
    bread = models.ForeignKey(Bread) 
    extras = models.ManyToManyField(Extra) 

    # ... 

    objects = DefaultSelectOrPrefetchManager(select_related=('bread',), prefetch_related=('extras',)) 

तो फिर तुम प्रबंधक आसानी से मॉडल वर्गों के बीच फिर से उपयोग कर सकते हैं:

+0

मॉडलों का उपयोग करते समय हम यह कैसे करते हैं .QuerySet? –

+0

मुझे लगता है कि आपको अधिक जानकारी, या कुछ उदाहरण कोड प्रदान करने की आवश्यकता है, जैसा कि आपका मतलब है; यदि आपके पास QuerySet है, तो आप सीधे 'select_related' पर कॉल कर सकते हैं। –

+0

मेरा मतलब था कि यदि आप ऑब्जेक्ट्स का उपयोग करते हैं = MyQuerySet.as_manager() –

2

एक कस्टम models.Manager बनाएं और सभी विधियों को ओवरराइड करें (filter, get आदि) और प्रत्येक क्वेरी पर select_related संलग्न करें। फिर मॉडल पर objects विशेषता के रूप में इस प्रबंधक को सेट करें।

मैं केवल आपके कोड के माध्यम से जाने और select_related जोड़ने की अनुशंसा करता हूं जहां आवश्यक हो, क्योंकि सबकुछ पर select_related करने से लाइन के नीचे कुछ गंभीर प्रदर्शन समस्याएं हो रही हैं (और यह पूरी तरह से स्पष्ट नहीं होगा कि यह कहां से आ रहा है)।

+0

फिर मुझे मॉडल फॉर्म में सभी फॉर्म फ़ील्ड ओवरराइड करना होगा ताकि मैं क्वेरीसेट मैन्युअल रूप से सेट कर सकूं ... – mpen

+0

ठीक है, आप हमेशा मेरे पहले सुझाव के साथ जा सकते हैं। मैं आपको लाइन के नीचे आने वाले संभावित हानिकारक प्रदर्शन मुद्दों के बारे में चेतावनी दे रहा था। सबकुछ ओवरराइड किए बिना 'मॉडलफॉर्म' के लिए इसे करने के तरीके हैं, लेकिन उत्तर वास्तव में इस बात पर निर्भर करेगा कि आपको वास्तव में क्या करना है। यदि आप इसके साथ मदद चाहते हैं, तो अधिक जानकारी के साथ बस एक और प्रश्न बनाएं। – sdolan

+0

ठीक है, मेरे पास एक पता मॉडल है जिसमें डाक कोड, शहर, प्रांत और देश के लिंक हैं। यह उन क्षेत्रों के बिना कभी भी प्रदर्शित नहीं होगा, इसलिए मैंने सोचा कि मैं इसे डिफ़ॉल्ट रूप से भी शामिल कर सकता हूं। – mpen

28

यहाँ भी एक मजेदार चाल है। एक उदाहरण के उपयोग के मामले के रूप में, यह उचित होगा यदि आपके पास मॉडल पर __unicode__ विधि है जो एक स्ट्रिंग प्रदान करता है जिसमें किसी संबंधित मॉडल से कुछ जानकारी शामिल होती है (या किसी अन्य मॉडल का मतलब लगभग हमेशा आवश्यक था)।

... और यदि आप वास्तव में निराला होना चाहते हैं, तो यहां एक और सामान्यीकृत संस्करण है। यह आपको args या kwargs के किसी भी संयोजन के साथ डिफ़ॉल्ट क्वेरीसेट पर विधियों के किसी अनुक्रम को कॉल करने की अनुमति देता है। कोड में कुछ त्रुटियां हो सकती हैं, लेकिन आपको विचार मिलता है।

from django.db import models 


class MethodCalls(object): 
    """ 
    A mock object which logs chained method calls. 
    """ 
    def __init__(self): 
     self._calls = [] 

    def __getattr__(self, name): 
     c = Call(self, name) 
     self._calls.append(c) 
     return c 

    def __iter__(self): 
     for c in self._calls: 
      yield tuple(c) 


class Call(object): 
    """ 
    Used by `MethodCalls` objects internally to represent chained method calls. 
    """ 
    def __init__(self, calls_obj, method_name): 
     self._calls = calls_obj 
     self.method_name = method_name 

    def __call__(self, *method_args, **method_kwargs): 
     self.method_args = method_args 
     self.method_kwargs = method_kwargs 

     return self._calls 

    def __iter__(self): 
     yield self.method_name 
     yield self.method_args 
     yield self.method_kwargs 


class DefaultQuerysetMethodCallsManager(models.Manager): 
    """ 
    A model manager class which allows specification of a sequence of 
    method calls to be applied by default to base querysets. 
    `DefaultQuerysetMethodCallsManager` instances expose a property 
    `default_queryset_method_calls` to which chained method calls can be 
    applied to indicate which methods should be called on base querysets. 
    """ 
    def __init__(self, *args, **kwargs): 
     self.default_queryset_method_calls = MethodCalls() 

     super(DefaultQuerysetMethodCallsManager, self).__init__(*args, **kwargs) 

    def get_queryset(self, *args, **kwargs): 
     qs = super(DefaultQuerysetMethodCallsManager, self).get_queryset(*args, **kwargs) 

     for method_name, method_args, method_kwargs in self.default_queryset_method_calls: 
      qs = getattr(qs, method_name)(*method_args, **method_kwargs) 

     return qs 


class Sandwich(models.Model): 
    bread = models.ForeignKey(Bread) 
    extras = models.ManyToManyField(Extra) 

    # Other field definitions... 

    objects = DefaultQuerysetMethodCallsManager() 
    objects.default_queryset_method_calls.filter(
     bread__type='wheat', 
    ).select_related(
     'bread', 
    ).prefetch_related(
     'extras', 
    ) 

अजगर-नकली प्रेरित MethodCalls वस्तु एपीआई और अधिक प्राकृतिक बनाने में एक प्रयास है। कुछ शायद थोड़ा उलझन में मिल सकता है। यदि ऐसा है, तो आप __init__ arg या kwarg के लिए उस कोड को उप-आउट कर सकते हैं जो विधि कॉल जानकारी की एक विधि को स्वीकार करता है।

+0

जैसे कुछ का उपयोग करें, यह वास्तव में मेरे दिन को बचाया -' मॉडलफॉर्म 'के टन लिखने के बजाय, गायों के घर आने तक 'ModelChoiceField' को ओवरराइड करना। (Espacially अगर 'MyModel .__ यूनिकोड __() 'का उपयोग करता है जिसे' select_related' होना चाहिए। –

+0

ग्रेट उत्तर। यह मेरे एन + 1 संबंधित मुद्दों के बहुमत को हल करता है – Eldamir

+1

नोट:' get_query_set' [Django 1.6 में बहिष्कृत किया गया था] (https : //docs.djangoproject.com/en/1.10/releases/1.6/#get-query-set-and-similar-methods-renamed-to-get-queryset)। इसे अब 'get_queryset' के साथ प्रतिस्थापित किया जाना चाहिए। – keithb

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