supposons que j'ai ce modèle:
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()
Maintenant, si je veux regarder efficacement un sous-ensemble de photos dans un sous-ensemble d'albums. Je fais quelque chose comme ça:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
Cela ne fait que deux requêtes, ce que j'attends (une pour obtenir les albums, puis une comme `SELECT * IN photos WHERE photoalbum_id IN ().
Tout est bon.
Mais si je fais ça:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
Ensuite, il fait une tonne de requêtes avec WHERE format = 1
! Est-ce que je fais quelque chose de mal ou est-ce que Django n'est pas assez intelligent pour se rendre compte qu'il a déjà récupéré toutes les photos et peut les filtrer en python? Je jure que j'ai lu quelque part dans la documentation qu'il est censé le faire ...
Réponses:
Dans Django 1.6 et les versions antérieures, il n'est pas possible d'éviter les requêtes supplémentaires. L'
prefetch_related
appel met effectivement en cache les résultats dea.photoset.all()
pour chaque album de l'ensemble de requêtes. Cependant, ila.photoset.filter(format=1)
s'agit d'un ensemble de requêtes différent, vous générerez donc une requête supplémentaire pour chaque album.Ceci est expliqué dans la
prefetch_related
documentation. Lefilter(format=1)
équivaut àfilter(spicy=True)
.Notez que vous pouvez réduire le nombre de requêtes en filtrant les photos en python à la place:
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]
Dans Django 1.7, il existe un
Prefetch()
objet qui vous permet de contrôler le comportement deprefetch_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
Pour plus d'exemples d'utilisation de l'
Prefetch
objet, consultez laprefetch_related
documentation.la source
À partir de la documentation :
Dans votre cas, "a.photo_set.filter (format = 1)" est traité comme une nouvelle requête.
De plus, "photo_set" est une recherche inversée - implémentée via un gestionnaire complètement différent.
la source
photo_set
peut également être préchargé avec.prefetch_related('photo_set')
. Mais l'ordre compte, comme vous l'avez expliqué.On peut l'utiliser
select_related
si vous voulez l'utiliser avec filter ()results = Geography.objects.filter(state__pk = 1).select_related('country') results.query
Pour en savoir plus: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related
la source