query - ¿Por qué el prefetch_related() de django solo funciona con all() y no filter()?
not in django query (2)
supongamos que tengo este modelo:
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()
Ahora si quiero ver un subconjunto de fotos en un subconjunto de álbumes de manera eficiente. Lo hago algo como esto:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
Esto solo hace dos consultas, que es lo que espero (una para obtener los álbumes, y luego una como `SELECT * IN fotos WHERE photoalbum_id IN ().
Todo esta bien.
Pero si hago esto:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
Entonces hace un montón de consultas con WHERE format = 1
! ¿Estoy haciendo algo mal o Django no es lo suficientemente inteligente como para darse cuenta de que ya ha captado todas las fotos y puede filtrarlas en Python? Juro que leí en algún lugar de la documentación que se supone que debe hacer eso ...
De los docs :
... como siempre con QuerySets, cualquier método encadenado posterior que implique una consulta de base de datos diferente ignorará los resultados previamente almacenados en caché, y recuperará los datos utilizando una nueva consulta de base de datos. Por lo tanto, si escribe lo siguiente:
pizzas = Pizza.objects.prefetch_related(''toppings'')
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
... entonces el hecho de que pizza.toppings.all () haya sido extraído previamente no lo ayudará; de hecho, perjudica el rendimiento, ya que ha realizado una consulta de base de datos que no ha utilizado. ¡Así que usa esta característica con precaución!
En su caso, "a.photo_set.filter (format = 1)" se trata como una nueva consulta.
Además, "photo_set" es una búsqueda inversa, implementada a través de un administrador diferente.
En Django 1.6 y versiones anteriores, no es posible evitar las consultas adicionales. La llamada prefetch_related
almacena en caché los resultados de a.photoset.all()
para cada álbum en el conjunto de consulta. Sin embargo, a.photoset.filter(format=1)
es un queryset diferente, por lo que generará una consulta adicional para cada álbum.
Esto se explica en los documentos prefetch_related
. El filter(format=1)
es equivalente a filter(spicy=True)
.
Tenga en cuenta que puede reducir el número o las consultas filtrando las fotos en python en su lugar:
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]
En Django 1.7, hay un objeto prefetch_related que le permite controlar el comportamiento de 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
Para obtener más ejemplos de cómo usar el objeto Prefetch
, consulte los documentos prefetch_related
.