Django'nun prefetch_related () öğesi neden yalnızca all () ile çalışır ve filter () ile çalışmaz?


90

bu modele sahip olduğumu varsayalım:

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()

Şimdi, bir albüm alt kümesindeki bir fotoğraf alt kümesine verimli bir şekilde bakmak istersem. Bunun gibi bir şey yapıyorum:

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

Bu sadece iki sorgu yapar, ki bu benim beklediğim şeydir (biri albümleri almak için ve sonra da `` SELECT * IN photos WHERE photoalbum_id IN () gibi.

Her şey harika.

Ama bunu yaparsam:

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

Sonra bir ton sorgu yapar WHERE format = 1! Yanlış bir şey mi yapıyorum yoksa django tüm fotoğrafları zaten getirdiğini ve onları python'da filtreleyebileceğini anlayacak kadar akıllı değil mi? Yemin ederim belgelerin bir yerinde bunu yapması gerektiğini okudum ...


Yanıtlar:


168

Django 1.6 ve önceki sürümlerde ekstra sorgulardan kaçınmak mümkün değildir. prefetch_relatedÇağrı etkin bir sonuçlarını önbelleğe a.photoset.all()sorgu kümesi her albüm için. Ancak, a.photoset.filter(format=1)farklı bir sorgu kümesidir, bu nedenle her albüm için fazladan bir sorgu oluşturursunuz.

Bu, prefetch_relatedbelgelerde açıklanmıştır . filter(format=1)Eşdeğerdir filter(spicy=True).

Bunun yerine fotoğrafları python'da filtreleyerek sayı veya sorguları azaltabileceğinizi unutmayın:

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'de, Prefetch()davranışını kontrol etmenizi sağlayan bir nesne vardır 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

PrefetchNesnenin nasıl kullanılacağına dair daha fazla örnek için prefetch_relateddokümanlara bakın .


8

Gönderen docs :

... QuerySets ile her zaman olduğu gibi, farklı bir veritabanı sorgusu anlamına gelen sonraki zincirleme yöntemler, önceden önbelleğe alınmış sonuçları yok sayar ve yeni bir veritabanı sorgusu kullanarak verileri alır. Öyleyse, aşağıdakileri yazarsanız:

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

... o zaman pizza.toppings.all () 'ın önceden getirilmiş olması size yardımcı olmaz - kullanmadığınız bir veritabanı sorgusu yaptığınız için aslında performansı düşürür. Bu yüzden bu özelliği dikkatli kullanın!

Sizin durumunuzda, "a.photo_set.filter (format = 1)" yeni bir sorgu gibi ele alınır.

Ek olarak, "photo_set", tamamen farklı bir yönetici aracılığıyla uygulanan bir ters aramadır.


photo_setile de önceden getirilebilir .prefetch_related('photo_set'). Ama açıkladığın gibi düzen önemlidir.
Risadinha

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.