2013-03-13 10 views
11

पर एकाधिक फ़ील्ड हम prefetch_related के साथ हमारे ऐप को तेज करने की कोशिश कर रहे हैं। यह GenericForeignKey संबंधों का पालन कर सकता है, और यह __ के साथ गहराई से जा सकता है लेकिन दुर्भाग्य से यह विफल हो जाएगा यदि संबंधित मॉडल में ऐसा क्षेत्र नहीं है।एक ही डीबी कॉलम

यहाँ मॉडल संरचना के कुछ उदाहरण

class ModelA(models.Model): 
    event_object = models.ForeignKey(SomeModelA) 

class ModelB(models.Model): 
    event = models.ForeignKey(SomeModelB) 

class ModelC(models.Model): 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    content_object = generic.GenericForeignKey() 

तो ModelC उदाहरण ModelA या ModelB के लिए या तो बात कर सकते हैं। और मैं दोनों ए और बी मॉडल prefetch करने के लिए इस तरह के क्वेरीसमूह उपयोग कर सकते हैं: ModelC.objects.all().prefetch_related('content_object') दुर्भाग्य से मैं भी घटना वस्तु का चयन करने की जरूरत है (SomeModelA या SomeModelB)

अगर मैं

ModelC.objects.all().prefetch_related('content_object', 'content_object__event_object') 

यह काम करेंगे चलाने का प्रयास करता है, तो मैं ModelC उदाहरण हैं जो ModelA पर इंगित करते हैं, लेकिन ओहटर मामले में यह असफल हो जाएगा क्योंकि ModelB में event_object फ़ील्ड नहीं है और इसके बजाय event है।

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

मैं इस तरह करने के लिए कोशिश कर रहा था:

class ModelB(models.Model): 
    event = models.ForeignKey(SomeModelB) 
    event_object = models.ForeignKey(SomeModelB, db_column='event_id', related_name='+') 

दो क्षेत्रों है कि डीबी तालिका में एक ही स्तंभ को इंगित करते हैं। हालांकि यह काम नहीं कर रहा है क्योंकि यह save विधि को तोड़ता है। Django एक UPDATE SQL क्वेरी बनाता है जहां एक कॉलम दो बार रखा जाता है और डेटाबेस एरर

ऐसे उपनाम बनाने का कोई तरीका है? या शायद prefetch_related को अपवाद फेंकने के लिए कोई और समाधान नहीं है?

अद्यतन: save विधि में वहाँ एक update_fields पैरामीटर है कि इस क्षेत्र को बाहर करने के लिए इस्तेमाल किया जा सकता है। हालांकि इसे 1.5 में पेश किया गया था और हम 1.4 का उपयोग कर रहे हैं। तो मैं एक जवाब खोजना जारी रखता हूं।

अद्यतन # 2: @ shx2 ने मुझे एक ट्रेसबैक प्रदान करने के लिए कहा। 2 संभावित ट्रेसबैक हैं। 1 - जब विशेषता पहली वस्तु पर याद आ रही है:

Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__ 
    data = list(self[:REPR_OUTPUT_SIZE + 1]) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__ 
    len(self) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__ 
    self._prefetch_related_objects() 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects 
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1664, in prefetch_related_objects 
    (attr, first_obj.__class__.__name__, lookup)) 
AttributeError: Cannot find 'event_object' on ModelB object, 'content_object__event_object' is an invalid parameter to prefetch_related() 

और अगर prefetch_related मापदंडों पहली वस्तु के लिए मान्य हैं तो मैं 2 ट्रैस बैक मिलता है:

Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 72, in __repr__ 
    data = list(self[:REPR_OUTPUT_SIZE + 1]) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 97, in __iter__ 
    len(self) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 89, in __len__ 
    self._prefetch_related_objects() 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 570, in _prefetch_related_objects 
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1680, in prefetch_related_objects 
    obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr) 
    File "/home/igor/workspace/projectname/eggs/Django-1.4.2-py2.7.egg/django/db/models/query.py", line 1803, in prefetch_one_level 
    qs = getattr(obj, attname).all() 
AttributeError: 'ModelB' object has no attribute 'event_object' 
+0

क्या आप 'prefetch_related' त्रुटि का ट्रेसबैक जोड़ सकते हैं? – shx2

+0

आपको मॉडलसी क्वेरीसेट की आवश्यकता क्यों है? क्या आप सिर्फ 2 अलग-अलग प्रश्न नहीं बना सकते हैं और उन्हें अलग से इलाज कर सकते हैं? मुझे पता है कि मेरा प्रश्न थोड़ा सा बेवकूफ़ है, लेकिन कभी-कभी समस्या यह है कि हम इस कठिनाइयों का सामना कैसे करते हैं – marianobianchi

+0

@marianobianchi नहीं, हम django व्यवस्थापक के कुछ हिस्से को अनुकूलित करने का प्रयास करते हैं, इसलिए हमें एक ही क्वेरीसेट की आवश्यकता है। इसके अलावा उपरोक्त मॉडल सरलीकृत हैं, वास्तविक परियोजना में हमारे गहरे संबंध हैं – Igor

उत्तर

2

यह एक बग या निरीक्षण की तरह दिखता है django में। एक कामकाज के रूप में, आप एक कस्टम मैनेजर को परिभाषित करने का प्रयास कर सकते हैं जो 2-चरण प्रीफेचिंग करता है।

from django.db import models 
from django.db.models import Q 
from django.contrib.contenttypes.models import ContentType 

class PrefetchWorkaroundManager(models.Manager): 
    def get_queryset(self): 
     q = super(PrefetchWorkaroundManager, self).get_queryset() 
     content_typeA = ContentType.objects.get_for_model(ModelA) 
     content_typeB = ContentType.objects.get_for_model(ModelB) 
     return q.filter(content_type__pk = content_typeA.id).prefetch_related('content_object', 'content_object__event_object') | \ 
       q.filter(content_type__pk = content_typeB.id).prefetch_related('content_object', 'content_object__event') 

class ModelC(models.Model): 
    ... 

    objects_prefetched = PrefetchWorkaroundManager() 

प्रत्येक फोन करने वाले जो जगह लेने के लिए ModelC.objects के बजाय ModelC.objects_prefetched का उपयोग करना चाहिए प्रीफेचिंग चाहता है:

ModelC.objects_prefetched.filter(...) 

मैं मानता हूं, मैं यह परीक्षण नहीं किया है, तो यह शायद के रूप में काम नहीं करता है। लेकिन मुझे विश्वास है कि यह दृष्टिकोण अच्छा है।

+0

उत्तर के लिए धन्यवाद, लेकिन दुर्भाग्यवश यह काम नहीं करता है। जब आप क्वेरीसेट पर '|' का उपयोग करते हैं तो यह एक नई क्वेरीसेट बनाता है। और 'prefetch_related' का मूल्यांकन तब किया जाता है जब क्वेरीसेट का मूल्यांकन किया जाता है। इसके अलावा मुझे यकीन नहीं है कि यह Django में बग है या नहीं, लेकिन जब आप '|' का उपयोग करते हैं तो यह केवल पहली क्वेरी से प्रीफेच लुकअप का उपयोग करता है – Igor

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