क्यों django के prefetch_related () केवल सभी के साथ काम करता है (और फ़िल्टर नहीं)?


89

मान लीजिए मेरे पास यह मॉडल है:

class PhotoAlbum(models.Model):
    title = models.CharField(max_length=128)
    author = models.CharField(max_length=128)

class Photo(models.Model):
    album = models.ForeignKey('PhotoAlbum')
    format = models.IntegerField()

अब अगर मैं एल्बम के सबसेट में फ़ोटो के सबसेट को कुशलतापूर्वक देखना चाहता हूँ। मैं इसे कुछ इस तरह से करता हूं:

someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
    somePhotos = a.photo_set.all()

यह केवल दो प्रश्न करता है, जो कि मैं उम्मीद करता हूं (एक एल्बम प्राप्त करने के लिए, और फिर एक का चयन करें जैसे फोटो में * फोटोलेब्युम_आईडी इन ()।

प्रत्येक वस्तु उत्तम हैं।

लेकिन अगर मैं ऐसा करता हूं:

someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
    somePhotos = a.photo_set.filter(format=1)

तो यह एक टन प्रश्नों के साथ करता है WHERE format = 1! क्या मैं कुछ गलत कर रहा हूं या django इतना स्मार्ट नहीं है कि यह महसूस कर सके कि वह पहले ही सभी तस्वीरें ले चुका है और उन्हें अजगर में फ़िल्टर कर सकता है? मैं कसम खाता हूं कि मैंने कहीं दस्तावेज में पढ़ा है कि ऐसा करना चाहिए ...


जवाबों:


166

Django 1.6 और पूर्व में, अतिरिक्त प्रश्नों से बचना संभव नहीं है। prefetch_relatedकॉल को प्रभावी ढंग से के परिणाम कैश a.photoset.all()क्वेरीसमूह में हर एल्बम के लिए। हालांकि, a.photoset.filter(format=1)एक अलग क्वेरीसेट है, इसलिए आप हर एल्बम के लिए एक अतिरिक्त क्वेरी उत्पन्न करेंगे।

यह prefetch_relatedडॉक्स में समझाया गया है । के filter(format=1)बराबर है filter(spicy=True)

ध्यान दें कि आप इसके बजाय अजगर में फोटो को फ़िल्टर करके संख्या या प्रश्नों को कम कर सकते हैं:

someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
    somePhotos = [p for p in a.photo_set.all() if p.format == 1]

Django 1.7 में, एक Prefetch()वस्तु है जो आपको के व्यवहार को नियंत्रित करने की अनुमति देती है prefetch_related

from django.db.models import Prefetch

someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related(
    Prefetch(
        "photo_set",
        queryset=Photo.objects.filter(format=1),
        to_attr="some_photos"
    )
)
for a in someAlbums:
    somePhotos = a.some_photos

Prefetchऑब्जेक्ट का उपयोग कैसे करें के अधिक उदाहरणों के लिए , prefetch_relatedडॉक्स देखें ।


8

से डॉक्स :

... हमेशा की तरह QuerySets के साथ, किसी भी बाद में जंजीर के तरीके जो एक अलग डेटाबेस क्वेरी का अर्थ है पहले कैश्ड परिणामों को अनदेखा करेंगे, और एक नए डेटाबेस क्वेरी का उपयोग करके डेटा पुनर्प्राप्त करेंगे। तो, यदि आप निम्नलिखित लिखते हैं:

pizzas = Pizza.objects.prefetch_related('toppings') [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]

... तो तथ्य यह है कि Pizza.toppings.all () पूर्वनिर्मित है आपकी मदद नहीं करेगा - वास्तव में यह प्रदर्शन को नुकसान पहुंचाता है, क्योंकि आपने डेटाबेस क्वेरी का उपयोग किया है जो आपने उपयोग नहीं किया है। इसलिए सावधानी के साथ इस सुविधा का उपयोग करें!

आपके मामले में, "a.photo_set.filter (स्वरूप = 1)" को एक ताजा प्रश्न के समान माना जाता है।

इसके अलावा, "photo_set" एक रिवर्स लुकअप है - एक अलग प्रबंधक के माध्यम से पूरी तरह से लागू किया गया।


photo_setप्रीफेट किया जा सकता है, साथ ही साथ .prefetch_related('photo_set')। लेकिन आदेश मामलों, जैसा कि आपने समझाया है।
रिसादीन

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